@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.
- package/LICENSE +21 -0
- package/README.md +10 -4
- package/ai-codex.ts +1 -0
- package/ai.ts +1 -0
- package/app-documents.ts +1 -0
- package/config-plugin.d.cts +2 -0
- package/contracts.ts +2 -0
- package/diagnostics.ts +1 -0
- package/expo-config.d.cts +1 -0
- package/metro.cjs +4 -1
- package/metro.d.cts +12 -0
- package/native.cjs +3 -1
- package/native.d.cts +6 -0
- package/package.json +82 -14
- package/schema.json +276 -58
- package/settings-observable.ts +1 -0
- package/settings-window.ts +1 -0
- package/shortcuts-commands.ts +1 -0
- package/shortcuts-keyboard.ts +3 -0
- package/ui-classnames.ts +1 -0
- package/ui-glass.ts +1 -0
- package/ui-search.ts +1 -0
- package/ui-sidebar.ts +1 -0
- package/ui-split-view.ts +1 -0
- package/ui-symbol.ts +1 -0
- package/vendor/node_modules/@legendapp/spark-ai/package.json +19 -0
- package/vendor/node_modules/@legendapp/spark-ai/src/availability.ts +17 -0
- package/vendor/node_modules/@legendapp/spark-ai/src/index.ts +15 -0
- package/vendor/node_modules/@legendapp/spark-ai/src/invocations.ts +34 -0
- package/vendor/node_modules/@legendapp/spark-ai/src/output.ts +62 -0
- package/vendor/node_modules/@legendapp/spark-ai/src/run.ts +12 -0
- package/vendor/node_modules/@legendapp/spark-ai/src/types.ts +16 -0
- package/vendor/node_modules/@legendapp/spark-audio/macos/RNSparkAudio.h +2 -1
- package/vendor/node_modules/@legendapp/spark-audio/macos/RNSparkAudio.mm +126 -24
- package/vendor/node_modules/@legendapp/spark-audio/package.json +5 -2
- package/vendor/node_modules/@legendapp/spark-audio/src/NativeSparkAudio.ts +5 -1
- package/vendor/node_modules/@legendapp/spark-audio/src/desktop.ts +36 -32
- package/vendor/node_modules/@legendapp/spark-audio/src/events.ts +11 -0
- package/vendor/node_modules/@legendapp/spark-audio/src/hooks.ts +65 -0
- package/vendor/node_modules/@legendapp/spark-audio/src/index.web.ts +92 -33
- package/vendor/node_modules/@legendapp/spark-audio/src/media-session-unavailable.ts +6 -4
- package/vendor/node_modules/@legendapp/spark-audio/src/media-session.ts +52 -19
- package/vendor/node_modules/@legendapp/spark-audio/src/media-session.web.ts +18 -10
- package/vendor/node_modules/@legendapp/spark-audio/src/media-types.ts +40 -13
- package/vendor/node_modules/@legendapp/spark-audio/src/mobile.ts +40 -32
- package/vendor/node_modules/@legendapp/spark-audio/src/player.ts +97 -0
- package/vendor/node_modules/@legendapp/spark-audio/src/status-listeners.ts +21 -15
- package/vendor/node_modules/@legendapp/spark-audio/src/types.ts +9 -4
- package/vendor/node_modules/@legendapp/spark-audio/windows/SparkAudio/SparkAudio.h +67 -20
- package/vendor/node_modules/@legendapp/spark-auth-session/package.json +4 -3
- package/vendor/node_modules/@legendapp/spark-auth-session/src/core.ts +60 -42
- package/vendor/node_modules/@legendapp/spark-auth-session/src/index.ts +28 -12
- package/vendor/node_modules/@legendapp/spark-auth-session/src/mobile.ts +97 -41
- package/vendor/node_modules/@legendapp/spark-auth-session/src/types.ts +20 -9
- package/vendor/node_modules/@legendapp/spark-cli/dist/add-desktop.js +50 -7
- package/vendor/node_modules/@legendapp/spark-cli/dist/build-mode.js +1 -1
- package/vendor/node_modules/@legendapp/spark-cli/dist/build.js +83 -48
- package/vendor/node_modules/@legendapp/spark-cli/dist/cli-arguments.js +60 -0
- package/vendor/node_modules/@legendapp/spark-cli/dist/commands.js +7 -2
- package/vendor/node_modules/@legendapp/spark-cli/dist/create.js +31 -5
- package/vendor/node_modules/@legendapp/spark-cli/dist/credentials.js +69 -23
- package/vendor/node_modules/@legendapp/spark-cli/dist/dev-command.js +12 -3
- package/vendor/node_modules/@legendapp/spark-cli/dist/dev.js +12 -9
- package/vendor/node_modules/@legendapp/spark-cli/dist/examples.js +1 -1
- package/vendor/node_modules/@legendapp/spark-cli/dist/expo-dev-patch.cjs +4 -2
- package/vendor/node_modules/@legendapp/spark-cli/dist/expo-metro.cjs +1 -1
- package/vendor/node_modules/@legendapp/spark-cli/dist/expo-node.js +3 -3
- package/vendor/node_modules/@legendapp/spark-cli/dist/index.js +10 -38
- package/vendor/node_modules/@legendapp/spark-cli/dist/local.js +1 -1
- package/vendor/node_modules/@legendapp/spark-cli/dist/metro.cjs +26 -5
- package/vendor/node_modules/@legendapp/spark-cli/dist/native-metadata.js +26 -0
- package/vendor/node_modules/@legendapp/spark-cli/dist/native-patch-preflight.js +84 -0
- package/vendor/node_modules/@legendapp/spark-cli/dist/native-preparation.js +88 -0
- package/vendor/node_modules/@legendapp/spark-cli/dist/notary-profiles.js +75 -0
- package/vendor/node_modules/@legendapp/spark-cli/dist/package.js +4 -2
- package/vendor/node_modules/@legendapp/spark-cli/dist/platform.js +16 -2
- package/vendor/node_modules/@legendapp/spark-cli/dist/project.js +66 -13
- package/vendor/node_modules/@legendapp/spark-cli/dist/release.js +84 -4
- package/vendor/node_modules/@legendapp/spark-cli/dist/release.json +23 -18
- package/vendor/node_modules/@legendapp/spark-cli/dist/runner-install.js +145 -31
- package/vendor/node_modules/@legendapp/spark-cli/dist/signing.js +4 -3
- package/vendor/node_modules/@legendapp/spark-cli/dist/updates.js +41 -4
- package/vendor/node_modules/@legendapp/spark-cli/dist/windows.js +4 -1
- package/vendor/node_modules/@legendapp/spark-cli/package.json +7 -6
- package/vendor/node_modules/@legendapp/spark-cli/templates/blank-typescript/App.tsx +6 -12
- package/vendor/node_modules/@legendapp/spark-cli/templates/blank-typescript/desktop.config.json +5 -3
- package/vendor/node_modules/@legendapp/spark-cli/templates/blank-typescript/package.json +11 -9
- package/vendor/node_modules/@legendapp/spark-cli/templates/diff-lite/Git.macos.tsx +2 -2
- package/vendor/node_modules/@legendapp/spark-cli/templates/document-editor/Integration.macos.tsx +23 -21
- package/vendor/node_modules/@legendapp/spark-cli/templates/document-editor/Integration.windows.tsx +23 -21
- package/vendor/node_modules/@legendapp/spark-cli/templates/document-editor/io.desktop.ts +11 -8
- package/vendor/node_modules/@legendapp/spark-cli/templates/document-editor/io.macos.ts +3 -3
- package/vendor/node_modules/@legendapp/spark-cli/templates/example-shared/Lifecycle.desktop.tsx +8 -14
- package/vendor/node_modules/@legendapp/spark-cli/templates/example-shared/OpenFiles.desktop.tsx +5 -10
- package/vendor/node_modules/@legendapp/spark-cli/templates/example-shared/io.desktop.ts +11 -8
- package/vendor/node_modules/@legendapp/spark-cli/templates/example-shared/io.macos.ts +3 -3
- package/vendor/node_modules/@legendapp/spark-cli/templates/music-lite/assets.desktop.ts +2 -2
- package/vendor/node_modules/@legendapp/spark-cli/templates/notes-lite/Activation.desktop.tsx +2 -2
- package/vendor/node_modules/@legendapp/spark-cli/templates/notes-lite/Desktop.desktop.ts +37 -15
- package/vendor/node_modules/@legendapp/spark-cli/templates/notes-lite/session.ts +5 -6
- package/vendor/node_modules/@legendapp/spark-cli/templates/notes-lite/window-session.ts +5 -4
- package/vendor/node_modules/@legendapp/spark-cli/templates/universal/desktop.config.json +5 -3
- package/vendor/node_modules/@legendapp/spark-cli/templates/universal/metro.config.js +1 -1
- package/vendor/node_modules/@legendapp/spark-cli/templates/universal/package.json +13 -11
- package/vendor/node_modules/@legendapp/spark-cli/templates/universal/react-native.config.js +1 -1
- package/vendor/node_modules/@legendapp/spark-cli/templates/windows/package.json +13 -11
- package/vendor/node_modules/@legendapp/spark-cli/templates/windows/react-native.config.js +1 -1
- package/vendor/node_modules/@legendapp/spark-clipboard/macos/RNDesktopClipboard.mm +10 -16
- package/vendor/node_modules/@legendapp/spark-clipboard/package.json +2 -2
- package/vendor/node_modules/@legendapp/spark-clipboard/src/NativeDesktopClipboard.ts +1 -1
- package/vendor/node_modules/@legendapp/spark-clipboard/src/desktop.ts +55 -31
- package/vendor/node_modules/@legendapp/spark-clipboard/src/expo.ts +21 -11
- package/vendor/node_modules/@legendapp/spark-clipboard/src/options.ts +8 -0
- package/vendor/node_modules/@legendapp/spark-clipboard/src/types.ts +10 -0
- package/vendor/node_modules/@legendapp/spark-clipboard/windows/SparkClipboard/SparkClipboard.h +38 -15
- package/vendor/node_modules/@legendapp/spark-clipboard/windows/SparkClipboard/SparkClipboard.vcxproj +2 -0
- package/vendor/node_modules/@legendapp/spark-codex/RNCodex.podspec +22 -0
- package/vendor/node_modules/@legendapp/spark-codex/cpp/HybridCodexAppServer.hpp +23 -0
- package/vendor/node_modules/@legendapp/spark-codex/cpp/HybridCodexAppServer.mm +1529 -0
- package/vendor/node_modules/@legendapp/spark-codex/nitro.json +21 -0
- package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/.gitattributes +1 -0
- package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/ios/RNCodex+autolinking.rb +62 -0
- package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/ios/RNCodex-Swift-Cxx-Bridge.cpp +17 -0
- package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/ios/RNCodex-Swift-Cxx-Bridge.hpp +27 -0
- package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/ios/RNCodex-Swift-Cxx-Umbrella.hpp +38 -0
- package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/ios/RNCodexAutolinking.mm +35 -0
- package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/ios/RNCodexAutolinking.swift +16 -0
- package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/shared/c++/CodexAvailability.hpp +95 -0
- package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/shared/c++/CodexRunResult.hpp +99 -0
- package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/shared/c++/HybridCodexAppServerSpec.cpp +24 -0
- package/vendor/node_modules/@legendapp/spark-codex/nitrogen/generated/shared/c++/HybridCodexAppServerSpec.hpp +71 -0
- package/vendor/node_modules/@legendapp/spark-codex/package.json +35 -0
- package/vendor/node_modules/@legendapp/spark-codex/react-native.config.js +11 -0
- package/vendor/node_modules/@legendapp/spark-codex/src/Codex.nitro.ts +30 -0
- package/vendor/node_modules/@legendapp/spark-codex/src/index.ts +65 -0
- package/vendor/node_modules/@legendapp/spark-codex/src/native.ts +6 -0
- package/vendor/node_modules/@legendapp/spark-commands/package.json +21 -0
- package/vendor/node_modules/@legendapp/spark-commands/src/bindings.ts +90 -0
- package/vendor/node_modules/@legendapp/spark-commands/src/index.tsx +400 -0
- package/vendor/node_modules/@legendapp/spark-commands/src/router.ts +138 -0
- package/vendor/node_modules/@legendapp/spark-commands/src/storage.ts +20 -0
- package/vendor/node_modules/@legendapp/spark-context-menu/macos/RNContextMenu.mm +7 -8
- package/vendor/node_modules/@legendapp/spark-context-menu/package.json +4 -3
- package/vendor/node_modules/@legendapp/spark-context-menu/src/NativeContextMenu.ts +1 -1
- package/vendor/node_modules/@legendapp/spark-context-menu/src/index.ts +26 -26
- package/vendor/node_modules/@legendapp/spark-context-menu/windows/SparkContextMenu/SparkContextMenu.h +3 -2
- package/vendor/node_modules/@legendapp/spark-desktop-app/RNDesktopApp.podspec +2 -1
- package/vendor/node_modules/@legendapp/spark-desktop-app/cpp/SparkBinaryJSI.hpp +89 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/cpp/SparkBinaryWindows.hpp +22 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/macos/RNDesktopApp.mm +13 -7
- package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkBinaryJSI.h +48 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkDesktop.h +4 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkDesktop.mm +33 -33
- package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkDesktopError.h +20 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkLifecycle.h +26 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkLifecycle.mm +49 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkQuitCoordinator.h +9 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkQuitCoordinator.mm +70 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/macos/SparkWindow.mm +9 -6
- package/vendor/node_modules/@legendapp/spark-desktop-app/macos/recent-documents/RNRecentDocumentController.mm +55 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/package.json +5 -3
- package/vendor/node_modules/@legendapp/spark-desktop-app/src/NativeDesktopApp.ts +1 -1
- package/vendor/node_modules/@legendapp/spark-desktop-app/src/api.ts +86 -40
- package/vendor/node_modules/@legendapp/spark-desktop-app/src/contracts/accelerator.ts +40 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/src/contracts/index.ts +56 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/src/contracts/menu.ts +114 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/src/contracts/native-buffer.ts +22 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/src/contracts/path.ts +24 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/src/events.ts +75 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/src/index.ts +2 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/src/index.windows.ts +2 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/src/open-requests.ts +43 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/src/restoration.ts +11 -0
- package/vendor/node_modules/@legendapp/spark-desktop-app/src/transport.ts +10 -0
- package/vendor/node_modules/@legendapp/spark-desktop-config/app.plugin.cjs +10 -5
- package/vendor/node_modules/@legendapp/spark-desktop-config/app.plugin.d.cts +3 -0
- package/vendor/node_modules/@legendapp/spark-desktop-config/autolinking.cjs +35 -0
- package/vendor/node_modules/@legendapp/spark-desktop-config/config.cjs +16 -3
- package/vendor/node_modules/@legendapp/spark-desktop-config/config.d.cts +89 -9
- package/vendor/node_modules/@legendapp/spark-desktop-config/expo.d.cts +3 -0
- package/vendor/node_modules/@legendapp/spark-desktop-config/identity.cjs +3 -2
- package/vendor/node_modules/@legendapp/spark-desktop-config/lifecycle.cjs +20 -0
- package/vendor/node_modules/@legendapp/spark-desktop-config/package.json +6 -5
- package/vendor/node_modules/@legendapp/spark-desktop-config/schema.json +276 -58
- package/vendor/node_modules/@legendapp/spark-desktop-config/updates.cjs +1 -0
- package/vendor/node_modules/@legendapp/spark-desktop-config/validation.cjs +35 -0
- package/vendor/node_modules/@legendapp/spark-desktop-config/windows.plugin.cjs +2 -1
- package/vendor/node_modules/@legendapp/spark-desktop-host/AppDelegate.mm +129 -11
- package/vendor/node_modules/@legendapp/spark-desktop-host/package.json +2 -2
- package/vendor/node_modules/@legendapp/spark-desktop-host/windows/application.inc +277 -121
- package/vendor/node_modules/@legendapp/spark-desktop-links/common/AuthLoopback.h +5 -7
- package/vendor/node_modules/@legendapp/spark-desktop-links/macos/RNDesktopLinks.mm +10 -9
- package/vendor/node_modules/@legendapp/spark-desktop-links/package.json +2 -2
- package/vendor/node_modules/@legendapp/spark-desktop-links/src/NativeDesktopLinks.ts +1 -1
- package/vendor/node_modules/@legendapp/spark-desktop-links/src/api.ts +27 -33
- package/vendor/node_modules/@legendapp/spark-desktop-links/src/expo.ts +3 -4
- package/vendor/node_modules/@legendapp/spark-desktop-links/src/native.ts +9 -0
- package/vendor/node_modules/@legendapp/spark-desktop-links/windows/SparkLinks/SparkLinks.h +22 -22
- package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/RNDesktopShortcuts.podspec +1 -0
- package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/macos/RNDesktopShortcuts.mm +1 -1
- package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/macos/keyboard-manager/RNKeyboardManager.h +9 -0
- package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/macos/keyboard-manager/RNKeyboardManager.mm +307 -0
- package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/package.json +8 -5
- package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/src/NativeDesktopShortcuts.ts +1 -1
- package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/src/api.ts +39 -14
- package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/src/keyboard-manager/NativeKeyboardManager.ts +12 -0
- package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/src/keyboard-manager/codes.ts +108 -0
- package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/src/keyboard-manager/index.ts +99 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/RNDesktopWindows.podspec +2 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/RNDesktopWindows.mm +229 -103
- package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/SparkWindowGeometry.h +63 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/window-manager/LegendWindowRegistry.h +20 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/window-manager/LegendWindowRegistry.mm +27 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/window-manager/RNWindowManager.h +12 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/window-manager/RNWindowManager.mm +3130 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/window-manager/RNWindowManagerStartup.mm +31 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/window-manager/SidebarStartupFallbacks.mm +14 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/package.json +10 -6
- package/vendor/node_modules/@legendapp/spark-desktop-windows/src/NativeDesktopWindowManager.ts +1 -1
- package/vendor/node_modules/@legendapp/spark-desktop-windows/src/api.ts +144 -70
- package/vendor/node_modules/@legendapp/spark-desktop-windows/src/diagnostics.ts +17 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/src/index.ts +3 -1
- package/vendor/node_modules/@legendapp/spark-desktop-windows/src/index.windows.ts +3 -1
- package/vendor/node_modules/@legendapp/spark-desktop-windows/src/macos-adapter.ts +53 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/src/macos.ts +51 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/src/transport.ts +41 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/src/types.ts +140 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/src/validation.ts +162 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/src/window-manager/NativeWindowManager.ts +28 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows/WindowProvider.tsx +20 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows/createWindowsNavigator.tsx +78 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows/index.ts +6 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows/primaryWindowLifecycle.ts +34 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows/types.ts +11 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows/usePrimaryWindowLifecycle.ts +18 -0
- package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows/useWindowFocusEffect.ts +17 -0
- package/vendor/node_modules/@legendapp/spark-documents/package.json +22 -0
- package/vendor/node_modules/@legendapp/spark-documents/src/controller.ts +97 -0
- package/vendor/node_modules/@legendapp/spark-documents/src/documentTransition.ts +13 -0
- package/vendor/node_modules/@legendapp/spark-documents/src/hooks.ts +139 -0
- package/vendor/node_modules/@legendapp/spark-documents/src/index.tsx +50 -0
- package/vendor/node_modules/@legendapp/spark-documents/src/reload.ts +39 -0
- package/vendor/node_modules/@legendapp/spark-documents/src/requests.ts +15 -0
- package/vendor/node_modules/@legendapp/spark-drag-drop/package.json +4 -3
- package/vendor/node_modules/@legendapp/spark-drag-drop/src/DesktopDragViewNativeComponent.ts +1 -1
- package/vendor/node_modules/@legendapp/spark-drag-drop/src/contracts.ts +73 -18
- package/vendor/node_modules/@legendapp/spark-drag-drop/src/index.tsx +58 -15
- package/vendor/node_modules/@legendapp/spark-drag-drop/windows/SparkDragDrop/SparkDragDrop.h +14 -8
- package/vendor/node_modules/@legendapp/spark-file-dialog/ios/RNFileDialog.mm +34 -121
- package/vendor/node_modules/@legendapp/spark-file-dialog/package.json +12 -5
- package/vendor/node_modules/@legendapp/spark-file-dialog/src/NativeFileDialog.ts +1 -5
- package/vendor/node_modules/@legendapp/spark-file-dialog/src/index.ts +19 -84
- package/vendor/node_modules/@legendapp/spark-file-dialog/src/native.ts +12 -0
- package/vendor/node_modules/@legendapp/spark-file-dialog/src/options.ts +34 -14
- package/vendor/node_modules/@legendapp/spark-file-dialog/src/types.ts +26 -0
- package/vendor/node_modules/@legendapp/spark-file-dialog/windows/SparkFileDialog/SparkFileDialog.h +42 -58
- package/vendor/node_modules/@legendapp/spark-file-system/RNDesktopFileSystem.podspec +1 -0
- package/vendor/node_modules/@legendapp/spark-file-system/macos/RNDesktopFileSystem.mm +32 -21
- package/vendor/node_modules/@legendapp/spark-file-system/macos/SparkFileMutations.h +24 -0
- package/vendor/node_modules/@legendapp/spark-file-system/macos/SparkFileMutations.mm +215 -0
- package/vendor/node_modules/@legendapp/spark-file-system/macos/file-scanner/RNFileScanner.h +9 -0
- package/vendor/node_modules/@legendapp/spark-file-system/macos/file-scanner/RNFileScanner.mm +82 -0
- package/vendor/node_modules/@legendapp/spark-file-system/macos/file-scanner/RNFileScannerCore.h +47 -0
- package/vendor/node_modules/@legendapp/spark-file-system/macos/file-scanner/RNFileScannerCore.mm +306 -0
- package/vendor/node_modules/@legendapp/spark-file-system/package.json +8 -6
- package/vendor/node_modules/@legendapp/spark-file-system/src/NativeDesktopFileSystem.ts +1 -1
- package/vendor/node_modules/@legendapp/spark-file-system/src/file-scanner/NativeFileScanner.ts +11 -0
- package/vendor/node_modules/@legendapp/spark-file-system/src/file-scanner/index.ts +93 -0
- package/vendor/node_modules/@legendapp/spark-file-system/src/handles.ts +48 -14
- package/vendor/node_modules/@legendapp/spark-file-system/src/index.ts +134 -45
- package/vendor/node_modules/@legendapp/spark-file-system/src/path.ts +1 -9
- package/vendor/node_modules/@legendapp/spark-file-system/windows/SparkFileSystem/SparkFileSystem.h +102 -26
- package/vendor/node_modules/@legendapp/spark-file-system/windows/SparkFileSystem/SparkFileSystem.vcxproj +2 -0
- package/vendor/node_modules/@legendapp/spark-global-shortcuts/RNDesktopGlobalShortcuts.podspec +1 -0
- package/vendor/node_modules/@legendapp/spark-global-shortcuts/macos/RNDesktopGlobalShortcuts.mm +7 -4
- package/vendor/node_modules/@legendapp/spark-global-shortcuts/package.json +4 -3
- package/vendor/node_modules/@legendapp/spark-global-shortcuts/src/NativeDesktopGlobalShortcuts.ts +1 -1
- package/vendor/node_modules/@legendapp/spark-global-shortcuts/src/index.ts +39 -10
- package/vendor/node_modules/@legendapp/spark-global-shortcuts/windows/SparkGlobalShortcuts/SparkGlobalShortcuts.h +5 -4
- package/vendor/node_modules/@legendapp/spark-message-dialog/macos/RNDesktopMessageDialog.mm +2 -2
- package/vendor/node_modules/@legendapp/spark-message-dialog/package.json +2 -2
- package/vendor/node_modules/@legendapp/spark-message-dialog/src/NativeDesktopMessageDialog.ts +1 -1
- package/vendor/node_modules/@legendapp/spark-message-dialog/src/index.ts +48 -11
- package/vendor/node_modules/@legendapp/spark-native-menu/ios/RNNativeMenu.mm +141 -609
- package/vendor/node_modules/@legendapp/spark-native-menu/package.json +9 -4
- package/vendor/node_modules/@legendapp/spark-native-menu/src/NativeMenu.ts +2 -7
- package/vendor/node_modules/@legendapp/spark-native-menu/src/api.ts +76 -121
- package/vendor/node_modules/@legendapp/spark-native-menu/src/hooks.ts +97 -0
- package/vendor/node_modules/@legendapp/spark-native-menu/src/windows-menus.ts +43 -37
- package/vendor/node_modules/@legendapp/spark-notifications/macos/RNDesktopNotifications.mm +75 -30
- package/vendor/node_modules/@legendapp/spark-notifications/package.json +2 -2
- package/vendor/node_modules/@legendapp/spark-notifications/src/NativeDesktopNotifications.ts +1 -1
- package/vendor/node_modules/@legendapp/spark-notifications/src/index.ts +130 -42
- package/vendor/node_modules/@legendapp/spark-notifications/windows/SparkNotifications/SparkNotifications.h +46 -8
- package/vendor/node_modules/@legendapp/spark-processes/macos/RNDesktopProcesses.mm +94 -21
- package/vendor/node_modules/@legendapp/spark-processes/package.json +4 -3
- package/vendor/node_modules/@legendapp/spark-processes/src/NativeDesktopProcesses.ts +1 -1
- package/vendor/node_modules/@legendapp/spark-processes/src/index.ts +124 -28
- package/vendor/node_modules/@legendapp/spark-processes/src/types.ts +34 -0
- package/vendor/node_modules/@legendapp/spark-processes/windows/SparkProcesses/SparkProcesses.h +83 -34
- package/vendor/node_modules/@legendapp/spark-processes/windows/SparkProcesses/SparkProcesses.vcxproj +2 -0
- package/vendor/node_modules/@legendapp/spark-secure-storage/macos/RNDesktopSecureStorage.mm +49 -40
- package/vendor/node_modules/@legendapp/spark-secure-storage/package.json +2 -2
- package/vendor/node_modules/@legendapp/spark-secure-storage/src/NativeDesktopSecureStorage.ts +1 -1
- package/vendor/node_modules/@legendapp/spark-secure-storage/src/desktop.ts +16 -14
- package/vendor/node_modules/@legendapp/spark-secure-storage/src/expo.ts +0 -1
- package/vendor/node_modules/@legendapp/spark-secure-storage/src/options.ts +3 -2
- package/vendor/node_modules/@legendapp/spark-secure-storage/src/unavailable.ts +6 -3
- package/vendor/node_modules/@legendapp/spark-settings/package.json +10 -3
- package/vendor/node_modules/@legendapp/spark-settings/src/fileStorage.ts +11 -0
- package/vendor/node_modules/@legendapp/spark-settings/src/index.ts +7 -9
- package/vendor/node_modules/@legendapp/spark-settings/src/storage/index.ts +142 -0
- package/vendor/node_modules/@legendapp/spark-settings/src/store.ts +63 -21
- package/vendor/node_modules/@legendapp/spark-settings-ui/package.json +20 -0
- package/vendor/node_modules/@legendapp/spark-settings-ui/src/index.tsx +436 -0
- package/vendor/node_modules/@legendapp/spark-settings-ui/src/options.ts +10 -0
- package/vendor/node_modules/@legendapp/spark-sqlite/package.json +3 -3
- package/vendor/node_modules/@legendapp/spark-sqlite/src/database.ts +182 -0
- package/vendor/node_modules/@legendapp/spark-sqlite/src/index.ts +19 -6
- package/vendor/node_modules/@legendapp/spark-sqlite/src/types.ts +22 -0
- package/vendor/node_modules/@legendapp/spark-system/macos/RNDesktopSystem.mm +36 -11
- package/vendor/node_modules/@legendapp/spark-system/package.json +2 -2
- package/vendor/node_modules/@legendapp/spark-system/src/NativeDesktopSystem.ts +1 -1
- package/vendor/node_modules/@legendapp/spark-system/src/index.ts +89 -32
- package/vendor/node_modules/@legendapp/spark-system/windows/SparkSystem/SparkSystem.h +8 -4
- package/vendor/node_modules/@legendapp/spark-tray/macos/RNDesktopTray.mm +20 -8
- package/vendor/node_modules/@legendapp/spark-tray/package.json +2 -2
- package/vendor/node_modules/@legendapp/spark-tray/src/NativeDesktopTray.ts +1 -1
- package/vendor/node_modules/@legendapp/spark-tray/src/index.ts +91 -34
- package/vendor/node_modules/@legendapp/spark-tray/windows/SparkTray/SparkTray.h +45 -15
- package/vendor/node_modules/@legendapp/spark-ui/macos/RNSparkButton.mm +2 -1
- package/vendor/node_modules/@legendapp/spark-ui/macos/RNSparkTextInput.mm +10 -2
- package/vendor/node_modules/@legendapp/spark-ui/macos/appkit-split-view/RNSidebarSplitViewComponent.h +15 -0
- package/vendor/node_modules/@legendapp/spark-ui/macos/appkit-split-view/RNSidebarSplitViewComponent.mm +1182 -0
- package/vendor/node_modules/@legendapp/spark-ui/macos/glass-effect-view/RNGlassEffectView.mm +72 -0
- package/vendor/node_modules/@legendapp/spark-ui/macos/native-select/RNNativeSegmentedControl.h +15 -0
- package/vendor/node_modules/@legendapp/spark-ui/macos/native-select/RNNativeSegmentedControl.mm +194 -0
- package/vendor/node_modules/@legendapp/spark-ui/macos/native-select/RNNativeSelect.h +15 -0
- package/vendor/node_modules/@legendapp/spark-ui/macos/native-select/RNNativeSelect.mm +180 -0
- package/vendor/node_modules/@legendapp/spark-ui/macos/sf-symbol/RNSFSymbol.h +15 -0
- package/vendor/node_modules/@legendapp/spark-ui/macos/sf-symbol/RNSFSymbol.mm +155 -0
- package/vendor/node_modules/@legendapp/spark-ui/macos/sidebar/RNSidebar.h +18 -0
- package/vendor/node_modules/@legendapp/spark-ui/macos/sidebar/RNSidebar.mm +645 -0
- package/vendor/node_modules/@legendapp/spark-ui/macos/text-input-search/RNTextInputSearch.h +15 -0
- package/vendor/node_modules/@legendapp/spark-ui/macos/text-input-search/RNTextInputSearch.mm +164 -0
- package/vendor/node_modules/@legendapp/spark-ui/package.json +16 -4
- package/vendor/node_modules/@legendapp/spark-ui/src/SparkSelectNativeComponent.ts +3 -1
- package/vendor/node_modules/@legendapp/spark-ui/src/SparkTextInputNativeComponent.ts +6 -2
- package/vendor/node_modules/@legendapp/spark-ui/src/appkit-split-view/SidebarSplitViewNativeComponent.ts +31 -0
- package/vendor/node_modules/@legendapp/spark-ui/src/appkit-split-view/index.tsx +77 -0
- package/vendor/node_modules/@legendapp/spark-ui/src/availability.ts +10 -0
- package/vendor/node_modules/@legendapp/spark-ui/src/classnames/index.ts +8 -0
- package/vendor/node_modules/@legendapp/spark-ui/src/control.ts +42 -0
- package/vendor/node_modules/@legendapp/spark-ui/src/desktop.tsx +56 -0
- package/vendor/node_modules/@legendapp/spark-ui/src/glass-effect-view/index.tsx +15 -0
- package/vendor/node_modules/@legendapp/spark-ui/src/index.android.tsx +35 -21
- package/vendor/node_modules/@legendapp/spark-ui/src/index.ios.tsx +55 -26
- package/vendor/node_modules/@legendapp/spark-ui/src/index.tsx +1 -27
- package/vendor/node_modules/@legendapp/spark-ui/src/index.web.tsx +45 -14
- package/vendor/node_modules/@legendapp/spark-ui/src/index.windows.tsx +1 -42
- package/vendor/node_modules/@legendapp/spark-ui/src/mobile-input.tsx +11 -0
- package/vendor/node_modules/@legendapp/spark-ui/src/native-select/NativeSegmentedControlNativeComponent.ts +17 -0
- package/vendor/node_modules/@legendapp/spark-ui/src/native-select/NativeSelectNativeComponent.ts +17 -0
- package/vendor/node_modules/@legendapp/spark-ui/src/select.ts +5 -3
- package/vendor/node_modules/@legendapp/spark-ui/src/sf-symbol/SFSymbolNativeComponent.ts +13 -0
- package/vendor/node_modules/@legendapp/spark-ui/src/sf-symbol/index.tsx +27 -0
- package/vendor/node_modules/@legendapp/spark-ui/src/sidebar/SidebarItemNativeComponent.ts +25 -0
- package/vendor/node_modules/@legendapp/spark-ui/src/sidebar/SidebarNativeComponent.ts +24 -0
- package/vendor/node_modules/@legendapp/spark-ui/src/sidebar/index.tsx +104 -0
- package/vendor/node_modules/@legendapp/spark-ui/src/specialized.ts +22 -0
- package/vendor/node_modules/@legendapp/spark-ui/src/text-input-search/TextInputSearchNativeComponent.ts +33 -0
- package/vendor/node_modules/@legendapp/spark-ui/src/text-input-search/index.tsx +34 -0
- package/vendor/node_modules/@legendapp/spark-ui/src/types.ts +30 -19
- package/vendor/node_modules/@legendapp/spark-ui/uniwind.ts +3 -1
- package/vendor/node_modules/@legendapp/spark-ui/uniwind.web.ts +3 -1
- package/vendor/node_modules/@legendapp/spark-ui/windows/SparkUI/SparkUI.h +16 -11
- package/vendor/node_modules/@legendapp/spark-updates/macos/RNDesktopUpdates.mm +21 -8
- package/vendor/node_modules/@legendapp/spark-updates/package.json +2 -2
- package/vendor/node_modules/@legendapp/spark-updates/src/NativeDesktopUpdates.ts +3 -2
- package/vendor/node_modules/@legendapp/spark-updates/src/index.ts +71 -15
- package/vendor/node_modules/@legendapp/spark-webview/package.json +2 -2
- package/vendor/node_modules/@legendapp/spark-webview/src/WebView.tsx +11 -0
- package/vendor/node_modules/@legendapp/spark-webview/src/adapter.ts +55 -0
- package/vendor/node_modules/@legendapp/spark-webview/src/index.ts +2 -2
- package/vendor/node_modules/@legendapp/spark-webview/src/types.ts +32 -0
- package/vendor/node_modules/@legendapp/spark-webview/src/view-props.ts +28 -0
- package/vendor/node_modules/@legendapp/spark-window-options/index.cjs +51 -14
- package/vendor/node_modules/@legendapp/spark-window-options/index.d.ts +21 -8
- package/vendor/node_modules/@legendapp/spark-window-options/package.json +1 -1
- package/windows-macos.ts +1 -0
- package/expo-metro.cjs +0 -1
- package/message-dialog.ts +0 -1
- package/universal.cjs +0 -1
- package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/src/accelerator.ts +0 -22
- package/vendor/node_modules/@legendapp/spark-desktop-windows/src/windows-options.ts +0 -11
- package/vendor/node_modules/@legendapp/spark-file-dialog/src/index.windows.ts +0 -15
- package/vendor/node_modules/@legendapp/spark-global-shortcuts/src/accelerator.ts +0 -22
- package/vendor/node_modules/@legendapp/spark-ui/macos/RNSparkSelect.h +0 -4
- package/vendor/node_modules/@legendapp/spark-ui/macos/RNSparkSelect.mm +0 -53
|
@@ -1,39 +1,135 @@
|
|
|
1
1
|
import { Platform } from "react-native";
|
|
2
|
+
import { callBinary, nativeBytes } from "@legendapp/spark-desktop-app/src/contracts/native-buffer";
|
|
2
3
|
import Native from "./NativeDesktopProcesses";
|
|
3
|
-
import { onDesktopEvent } from "@legendapp/spark-desktop-app";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
import { onDesktopEvent } from "@legendapp/spark-desktop-app/src/events";
|
|
5
|
+
import { SparkError, invokeNative, nativeError, type Availability } from "@legendapp/spark-desktop-app/src/contracts";
|
|
6
|
+
import { nativePath } from "@legendapp/spark-desktop-app/src/contracts/path";
|
|
7
|
+
import type { ProcessOptions, RunCommandOptions, ProcessInput, ProcessResult, ProcessHandle } from "./types";
|
|
8
|
+
export type * from "./types";
|
|
9
|
+
export function getProcessAvailability(): Availability {
|
|
10
|
+
if (Platform.OS !== "macos" && Platform.OS !== "windows") return { available: false, reason: "unsupported-platform" };
|
|
11
|
+
return Native ? { available: true } : { available: false, reason: "missing-module" };
|
|
12
|
+
}
|
|
13
|
+
async function call<T>(method: string, args: object, validate: (value: unknown) => value is T): Promise<T> {
|
|
14
|
+
if (Platform.OS !== "macos" && Platform.OS !== "windows") throw new SparkError("E_UNSUPPORTED_PLATFORM", "Processes require a desktop host");
|
|
15
|
+
if (!Native) throw new SparkError("E_MODULE_UNAVAILABLE", "Process module is unavailable");
|
|
16
|
+
const { bytes, ...metadata } = args as { bytes?: Uint8Array };
|
|
17
|
+
const value = await invokeNative(() => callBinary(Native!, "__sparkProcessesBinary", method, metadata, bytes));
|
|
18
|
+
if (!validate(value)) throw new SparkError("E_INVALID_DATA", "Invalid native process response");
|
|
19
|
+
return value;
|
|
20
|
+
}
|
|
21
|
+
const command = (method: string, args: object) => call(method, args, (value): value is null => value === null).then(() => {});
|
|
22
|
+
function invalid(message: string): never { throw new SparkError("E_INVALID_ARGUMENT", message); }
|
|
23
|
+
function object(value: unknown, allowed: string[]): asserts value is Record<string, unknown> {
|
|
24
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) invalid("Expected process options");
|
|
25
|
+
for (const key of Object.keys(value)) if (!allowed.includes(key)) throw new SparkError("E_UNSUPPORTED_OPTION", `Unknown process option: ${key}`);
|
|
26
|
+
}
|
|
27
|
+
function commandName(value: unknown): asserts value is string {
|
|
28
|
+
if (typeof value !== "string" || !value || /[\\/\0:]/.test(value) || value === "." || value === ".." || value.trim() !== value) invalid("Expected a command name without path components");
|
|
29
|
+
}
|
|
30
|
+
export async function resolveCommand(name: string): Promise<string | null> {
|
|
31
|
+
commandName(name);
|
|
32
|
+
const value = await call("resolveCommand", { command: name }, (value): value is string | null => value === null || typeof value === "string");
|
|
33
|
+
if (value === null) return null;
|
|
34
|
+
try { return nativePath(value, Platform.OS); } catch (cause) { throw new SparkError("E_INVALID_DATA", "Resolved command path is invalid", { cause }); }
|
|
35
|
+
}
|
|
36
|
+
function inputBytes(input: ProcessInput): Uint8Array {
|
|
37
|
+
if (typeof input === "string") return new TextEncoder().encode(input);
|
|
38
|
+
if (!(input instanceof Uint8Array)) invalid("Process input must be UTF-8 text or Uint8Array");
|
|
39
|
+
return input;
|
|
40
|
+
}
|
|
41
|
+
function result(value: unknown, aborted: boolean, limit: number): ProcessResult {
|
|
42
|
+
if (!value || typeof value !== "object") throw new SparkError("E_INVALID_DATA", "Invalid process exit result");
|
|
43
|
+
const data = value as Record<string, unknown>;
|
|
44
|
+
if (!Number.isInteger(data.exitCode) || (data.exitCode as number) < 0 || (data.exitCode as number) > 0xffffffff || typeof data.terminated !== "boolean" || !(data.terminationSignal === null || (Number.isInteger(data.terminationSignal) && (data.terminationSignal as number) > 0)) || (!data.terminated && data.terminationSignal !== null) || typeof data.timedOut !== "boolean" || typeof data.outputTruncated !== "boolean") throw new SparkError("E_INVALID_DATA", "Invalid process exit status");
|
|
45
|
+
return { exit: data.terminated ? { type: "terminated", signal: data.terminationSignal as number | null } : { type: "exited", code: data.exitCode as number }, stdout: nativeBytes(data.stdout, limit), stderr: nativeBytes(data.stderr, limit), timedOut: data.timedOut, outputTruncated: data.outputTruncated, aborted };
|
|
46
|
+
}
|
|
7
47
|
let sequence = 0;
|
|
8
|
-
function
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
if (
|
|
14
|
-
if (
|
|
15
|
-
if (
|
|
16
|
-
|
|
48
|
+
export async function spawn(options: ProcessOptions): Promise<ProcessHandle> {
|
|
49
|
+
object(options, ["target", "args", "cwd", "env", "input", "timeoutMs", "signal", "captureLimitBytes", "onOutput"]);
|
|
50
|
+
object(options.target, ["type", "path", "name"]);
|
|
51
|
+
const { target, signal, onOutput } = options;
|
|
52
|
+
let executable: string;
|
|
53
|
+
if (target.type === "executable") { object(target, ["type", "path"]); executable = nativePath(target.path, Platform.OS); }
|
|
54
|
+
else if (target.type === "helper") { object(target, ["type", "name"]); if (typeof target.name !== "string" || !/^[A-Za-z0-9_-]+$/.test(target.name)) invalid("Invalid helper name"); executable = `helper:${target.name}`; }
|
|
55
|
+
else if (target.type === "command") { object(target, ["type", "name"]); commandName(target.name); executable = target.name; }
|
|
56
|
+
else invalid("Unknown process target type");
|
|
57
|
+
if (options.args !== undefined && (!Array.isArray(options.args) || options.args.some(arg => typeof arg !== "string" || arg.includes("\0")))) invalid("Invalid process arguments");
|
|
58
|
+
const cwd = options.cwd === undefined ? undefined : nativePath(options.cwd, Platform.OS);
|
|
59
|
+
if (options.env !== undefined) {
|
|
60
|
+
if (!options.env || typeof options.env !== "object" || Array.isArray(options.env) || Object.entries(options.env).some(([key, value]) => !key || /[=\0]/.test(key) || typeof value !== "string" || value.includes("\0"))) invalid("Invalid process environment");
|
|
61
|
+
}
|
|
62
|
+
if (options.timeoutMs !== undefined && (!Number.isInteger(options.timeoutMs) || options.timeoutMs < 1 || options.timeoutMs > 0xfffffffe)) invalid("timeoutMs must be an integer from 1 to 4294967294");
|
|
63
|
+
const captureLimitBytes = options.captureLimitBytes ?? 8 * 1024 * 1024;
|
|
64
|
+
if (!Number.isInteger(captureLimitBytes) || captureLimitBytes < 0 || captureLimitBytes > 8 * 1024 * 1024) invalid("captureLimitBytes must be an integer from 0 to 8388608");
|
|
65
|
+
if (onOutput !== undefined && typeof onOutput !== "function") invalid("onOutput must be a function");
|
|
66
|
+
if (signal !== undefined && (!signal || typeof signal.aborted !== "boolean" || typeof signal.addEventListener !== "function" || typeof signal.removeEventListener !== "function")) invalid("Expected an AbortSignal");
|
|
67
|
+
const wire = { executable, command: target.type === "command", args: options.args ?? [], cwd, env: options.env ?? {}, bytes: options.input === undefined ? undefined : inputBytes(options.input), timeoutMs: options.timeoutMs, captureLimitBytes, streamOutput: !!onOutput };
|
|
68
|
+
if (signal?.aborted) throw new SparkError("E_ABORTED", "Process launch was aborted");
|
|
69
|
+
const availability = getProcessAvailability();
|
|
70
|
+
if (!availability.available) throw new SparkError(availability.reason === "missing-module" ? "E_MODULE_UNAVAILABLE" : "E_UNSUPPORTED_PLATFORM", "Process execution is unavailable");
|
|
17
71
|
const id = `process-${Date.now()}-${++sequence}`;
|
|
18
|
-
let finish!: (value: ProcessResult) => void;
|
|
19
|
-
let ended = false;
|
|
20
|
-
|
|
72
|
+
let finish!: (value: ProcessResult) => void, fail!: (error: unknown) => void;
|
|
73
|
+
let ended = false, aborted = false, started = false, inputClosing = false, outputStopped = false;
|
|
74
|
+
let closingInput: Promise<void> | undefined, terminating: Promise<void> | undefined;
|
|
75
|
+
let settleExit!: () => void;
|
|
76
|
+
const exitObserved = new Promise<void>(resolve => { settleExit = resolve; });
|
|
77
|
+
const exited = new Promise<ProcessResult>((resolve, reject) => { finish = resolve; fail = reject; });
|
|
78
|
+
// Exit can precede the spawn acknowledgement, before callers receive the handle.
|
|
79
|
+
void exited.catch(() => {});
|
|
80
|
+
function abort() { if (!ended) { aborted = true; if (started) void terminate().catch(fail); } }
|
|
81
|
+
function terminate(): Promise<void> {
|
|
82
|
+
if (ended) return Promise.resolve();
|
|
83
|
+
return terminating ??= command("terminate", { id }).then(() => exitObserved).catch(error => { terminating = undefined; throw error; });
|
|
84
|
+
}
|
|
21
85
|
const subscription = onDesktopEvent(event => {
|
|
22
|
-
if (event.processId !== id) return;
|
|
23
|
-
if (event.type === "
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
86
|
+
if (event.processId !== id || ended) return;
|
|
87
|
+
if (event.type === "processExit") {
|
|
88
|
+
ended = true; subscription.remove(); signal?.removeEventListener("abort", abort); settleExit();
|
|
89
|
+
try { finish(result(event.result, aborted, captureLimitBytes)); } catch (error) { fail(error); }
|
|
90
|
+
} else if (event.type === "processOutput" && !outputStopped) {
|
|
91
|
+
try {
|
|
92
|
+
if (event.stream !== "stdout" && event.stream !== "stderr") throw new SparkError("E_INVALID_DATA", "Invalid process output stream");
|
|
93
|
+
const bytes = nativeBytes(event.bytes, 16384);
|
|
94
|
+
onOutput?.({ stream: event.stream, bytes });
|
|
95
|
+
} catch (error) { outputStopped = true; fail(nativeError(error)); void terminate().catch(fail); }
|
|
96
|
+
}
|
|
97
|
+
}, { types: ["processOutput", "processExit"], target: { field: "processId", value: id } });
|
|
98
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
99
|
+
try {
|
|
100
|
+
await command("spawn", { id, ...wire }); started = true;
|
|
101
|
+
if (signal?.aborted) abort();
|
|
102
|
+
if (aborted) void terminate().catch(fail);
|
|
103
|
+
} catch (error) {
|
|
104
|
+
subscription.remove(); signal?.removeEventListener("abort", abort);
|
|
105
|
+
// A malformed acknowledgement can follow successful allocation.
|
|
106
|
+
if (error instanceof SparkError && error.code === "E_INVALID_DATA") {
|
|
107
|
+
try { await command("terminate", { id }); }
|
|
108
|
+
catch (cleanup) { throw new SparkError("E_NATIVE", "Process launch and termination failed", { cause: new AggregateError([error, cleanup]) }); }
|
|
109
|
+
}
|
|
110
|
+
throw error;
|
|
111
|
+
}
|
|
28
112
|
return {
|
|
29
113
|
id, exited,
|
|
30
|
-
async write(
|
|
31
|
-
|
|
32
|
-
|
|
114
|
+
async write(input) {
|
|
115
|
+
const bytes = inputBytes(input);
|
|
116
|
+
if (ended || inputClosing) throw new SparkError("E_CLOSED", "Process input is closed");
|
|
117
|
+
return command("write", { id, bytes });
|
|
118
|
+
},
|
|
119
|
+
closeInput() {
|
|
120
|
+
inputClosing = true;
|
|
121
|
+
if (ended) return Promise.resolve();
|
|
122
|
+
return closingInput ??= command("closeInput", { id }).catch(error => { closingInput = undefined; throw error; });
|
|
123
|
+
},
|
|
124
|
+
terminate,
|
|
33
125
|
};
|
|
34
126
|
}
|
|
35
|
-
export async function runCommand(options:
|
|
127
|
+
export async function runCommand(options: RunCommandOptions): Promise<ProcessResult> {
|
|
36
128
|
const child = await spawn(options);
|
|
37
|
-
await child.closeInput();
|
|
38
|
-
|
|
129
|
+
try { await child.closeInput(); return await child.exited; }
|
|
130
|
+
catch (error) {
|
|
131
|
+
try { await child.terminate(); }
|
|
132
|
+
catch (cleanup) { throw new SparkError("E_NATIVE", "Command and termination failed", { cause: new AggregateError([error, cleanup]) }); }
|
|
133
|
+
throw error;
|
|
134
|
+
}
|
|
39
135
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export type ProcessTarget = { type: "executable"; path: string } | { type: "helper"; name: string } | { type: "command"; name: string };
|
|
2
|
+
export type ProcessInput = string | Uint8Array;
|
|
3
|
+
export type ProcessOutput = { stream: "stdout" | "stderr"; bytes: Uint8Array };
|
|
4
|
+
export type ProcessOptions = {
|
|
5
|
+
target: ProcessTarget;
|
|
6
|
+
args?: readonly string[];
|
|
7
|
+
cwd?: string;
|
|
8
|
+
env?: Readonly<Record<string, string>>;
|
|
9
|
+
input?: ProcessInput;
|
|
10
|
+
timeoutMs?: number;
|
|
11
|
+
signal?: AbortSignal;
|
|
12
|
+
/** Maximum captured bytes per stream, 0–8 MiB; streaming remains complete. */
|
|
13
|
+
captureLimitBytes?: number;
|
|
14
|
+
onOutput?: (output: ProcessOutput) => void;
|
|
15
|
+
};
|
|
16
|
+
export type RunCommandOptions = ProcessOptions;
|
|
17
|
+
export type ProcessExit = { type: "exited"; code: number } | { type: "terminated"; signal: number | null };
|
|
18
|
+
export type ProcessResult = {
|
|
19
|
+
exit: ProcessExit;
|
|
20
|
+
stdout: Uint8Array;
|
|
21
|
+
stderr: Uint8Array;
|
|
22
|
+
timedOut: boolean;
|
|
23
|
+
aborted: boolean;
|
|
24
|
+
outputTruncated: boolean;
|
|
25
|
+
};
|
|
26
|
+
export interface ProcessHandle {
|
|
27
|
+
readonly id: string;
|
|
28
|
+
readonly exited: Promise<ProcessResult>;
|
|
29
|
+
/** Strings use UTF-8; byte arrays pass through unchanged. Calls preserve order. */
|
|
30
|
+
write(input: ProcessInput): Promise<void>;
|
|
31
|
+
closeInput(): Promise<void>;
|
|
32
|
+
/** Resolve after the process tree exits and its output drains. Retry on failure. */
|
|
33
|
+
terminate(): Promise<void>;
|
|
34
|
+
}
|
package/vendor/node_modules/@legendapp/spark-processes/windows/SparkProcesses/SparkProcesses.h
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#pragma once
|
|
2
2
|
#include "NativeModules.h"
|
|
3
|
+
#include <SparkBinaryWindows.hpp>
|
|
3
4
|
#include <algorithm>
|
|
4
5
|
#include <atomic>
|
|
5
6
|
#include <condition_variable>
|
|
6
7
|
#include <deque>
|
|
7
8
|
#include <filesystem>
|
|
8
|
-
#include <future>
|
|
9
9
|
#include <fstream>
|
|
10
10
|
#include <map>
|
|
11
11
|
#include <memory>
|
|
@@ -13,9 +13,7 @@
|
|
|
13
13
|
#include <optional>
|
|
14
14
|
#include <thread>
|
|
15
15
|
#include <vector>
|
|
16
|
-
#include <wincrypt.h>
|
|
17
16
|
#include <winrt/Windows.Data.Json.h>
|
|
18
|
-
#pragma comment(lib, "Crypt32.lib")
|
|
19
17
|
namespace winrt::SparkProcesses {
|
|
20
18
|
namespace React = Microsoft::ReactNative;
|
|
21
19
|
namespace Json = Windows::Data::Json;
|
|
@@ -27,47 +25,75 @@ inline std::wstring Quote(std::wstring const &argument) {
|
|
|
27
25
|
}
|
|
28
26
|
result.append(slashes * 2, L'\\'); result += L'"'; return result;
|
|
29
27
|
}
|
|
30
|
-
inline std::
|
|
31
|
-
if (
|
|
32
|
-
DWORD
|
|
33
|
-
|
|
28
|
+
inline std::wstring ResolveCommand(std::wstring const &name) {
|
|
29
|
+
if (name.empty() || name.find_first_of(L"/\\:") != std::wstring::npos) throw hresult_invalid_argument(L"Expected a command name");
|
|
30
|
+
DWORD size = GetEnvironmentVariableW(L"PATH", nullptr, 0);
|
|
31
|
+
if (!size) return {};
|
|
32
|
+
std::wstring paths(size, L'\0'); GetEnvironmentVariableW(L"PATH", paths.data(), size);
|
|
33
|
+
std::wstring absolutePaths; size_t start = 0;
|
|
34
|
+
while (start < paths.size()) {
|
|
35
|
+
auto end = paths.find(L';', start); auto directory = paths.substr(start, end == std::wstring::npos ? std::wstring::npos : end - start);
|
|
36
|
+
directory.resize(wcslen(directory.c_str()));
|
|
37
|
+
if (directory.size() >= 2 && directory.front() == L'"' && directory.back() == L'"') directory = directory.substr(1, directory.size() - 2);
|
|
38
|
+
if (std::filesystem::path(directory).is_absolute()) { if (!absolutePaths.empty()) absolutePaths += L';'; absolutePaths += directory; }
|
|
39
|
+
if (end == std::wstring::npos) break; start = end + 1;
|
|
40
|
+
}
|
|
41
|
+
if (absolutePaths.empty()) return {};
|
|
42
|
+
wchar_t result[32768]{};
|
|
43
|
+
DWORD count = SearchPathW(absolutePaths.c_str(), name.c_str(), L".exe", 32768, result, nullptr);
|
|
44
|
+
if (!count) return {};
|
|
45
|
+
if (count >= 32768) throw hresult_invalid_argument(L"Resolved command path is too long");
|
|
46
|
+
auto attributes = GetFileAttributesW(result);
|
|
47
|
+
if (attributes == INVALID_FILE_ATTRIBUTES || (attributes & FILE_ATTRIBUTE_DIRECTORY)) return {};
|
|
48
|
+
auto extension = std::filesystem::path(result).extension().wstring();
|
|
49
|
+
if (_wcsicmp(extension.c_str(), L".exe") && _wcsicmp(extension.c_str(), L".com")) return {};
|
|
50
|
+
return result;
|
|
34
51
|
}
|
|
35
|
-
|
|
52
|
+
class CapturedBytes final : public spark::binary::jsi::MutableBuffer {
|
|
53
|
+
std::string bytes;
|
|
54
|
+
public:
|
|
55
|
+
explicit CapturedBytes(std::string value) : bytes(std::move(value)) {}
|
|
56
|
+
size_t size() const override { return bytes.size(); }
|
|
57
|
+
uint8_t *data() override { return reinterpret_cast<uint8_t *>(bytes.data()); }
|
|
58
|
+
};
|
|
36
59
|
struct Process : std::enable_shared_from_this<Process> {
|
|
37
|
-
struct Input { std::
|
|
60
|
+
struct Input { std::vector<uint8_t> text; bool close; std::optional<spark::binary::NativePromise> promise; };
|
|
38
61
|
handle process, job, input, output, error;
|
|
39
62
|
std::mutex mutex; std::condition_variable condition; std::deque<Input> queue;
|
|
40
63
|
std::atomic<bool> done{false}, terminated{false}, timedOut{false}, truncated{false};
|
|
41
64
|
bool inputClosed = false;
|
|
65
|
+
size_t captureLimit = 8 * 1024 * 1024;
|
|
42
66
|
std::string id, stdoutBytes, stderrBytes;
|
|
43
67
|
React::ReactContext context;
|
|
68
|
+
void StopChecked() { if (job && !TerminateJobObject(job.get(), 1)) throw_last_error(); terminated = true; }
|
|
44
69
|
void Stop() noexcept { terminated = true; if (job) TerminateJobObject(job.get(), 1); }
|
|
45
|
-
void Write(Input value) { std::lock_guard lock(mutex); if (done || inputClosed) { if (value.promise) { if (value.close) value.promise->Resolve("null"); else value.promise->Reject(React::ReactError{"
|
|
70
|
+
void Write(Input value) { std::lock_guard lock(mutex); if (done || inputClosed) { if (value.promise) { if (value.close) value.promise->Resolve("null"); else value.promise->Reject(React::ReactError{"E_CLOSED", "Process input is closed"}); } return; } if (value.close) inputClosed = true; queue.push_back(std::move(value)); condition.notify_one(); }
|
|
46
71
|
void InputLoop() noexcept {
|
|
47
72
|
while (true) {
|
|
48
73
|
Input value;
|
|
49
74
|
{ std::unique_lock lock(mutex); condition.wait(lock, [&]() { return done || !queue.empty(); }); if (queue.empty()) break; value = std::move(queue.front()); queue.pop_front(); }
|
|
50
|
-
if (done || !input) { if (value.promise) value.promise->Reject(React::ReactError{"
|
|
75
|
+
if (done || !input) { if (value.promise) value.promise->Reject(React::ReactError{"E_CLOSED", "Process input is closed"}); continue; }
|
|
51
76
|
bool success = true;
|
|
52
77
|
if (value.close) input.close();
|
|
53
78
|
else for (size_t offset = 0; offset < value.text.size();) { DWORD written = 0; if (!WriteFile(input.get(), value.text.data() + offset, static_cast<DWORD>(std::min<size_t>(16384, value.text.size() - offset)), &written, nullptr) || !written) { success = false; break; } offset += written; }
|
|
54
|
-
if (value.promise) { if (success) value.promise->Resolve("null"); else value.promise->Reject(React::ReactError{"
|
|
79
|
+
if (value.promise) { if (success) value.promise->Resolve("null"); else value.promise->Reject(React::ReactError{"E_CLOSED", "Process input is closed"}); }
|
|
55
80
|
}
|
|
56
81
|
input.close();
|
|
57
82
|
}
|
|
58
83
|
void Read(HANDLE pipe, std::string &buffer, std::string stream, bool streaming, std::shared_ptr<std::atomic<bool>> active) {
|
|
84
|
+
struct Credits { std::mutex mutex; std::condition_variable ready; size_t pending = 0; };
|
|
85
|
+
auto credits = std::make_shared<Credits>();
|
|
59
86
|
char bytes[16384]; DWORD count;
|
|
60
87
|
while (ReadFile(pipe, bytes, sizeof(bytes), &count, nullptr) && count) {
|
|
61
|
-
const auto keep = std::min<size_t>(count,
|
|
88
|
+
const auto keep = std::min<size_t>(count, captureLimit - buffer.size()); buffer.append(bytes, keep); if (keep < count) truncated = true;
|
|
62
89
|
if (streaming && *active) {
|
|
63
|
-
|
|
64
|
-
auto data =
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
90
|
+
{ std::unique_lock lock(credits->mutex); while (*active && credits->pending >= 64) credits->ready.wait_for(lock, std::chrono::milliseconds(100)); if (!*active) break; ++credits->pending; }
|
|
91
|
+
auto data = std::make_shared<spark::binary::Bytes>(std::vector<uint8_t>(bytes, bytes + count)); auto key = id;
|
|
92
|
+
auto delivered = [credits] { { std::lock_guard lock(credits->mutex); --credits->pending; } credits->ready.notify_one(); };
|
|
93
|
+
context.UIDispatcher().Post([context = context, active, key, stream, data, delivered]() {
|
|
94
|
+
if (*active) { Json::JsonObject event; event.SetNamedValue(L"type", Json::JsonValue::CreateStringValue(L"processOutput")); event.SetNamedValue(L"processId", Json::JsonValue::CreateStringValue(to_hstring(key))); event.SetNamedValue(L"stream", Json::JsonValue::CreateStringValue(to_hstring(stream))); spark::binary::Response response{to_string(event.Stringify()), {}, {{"bytes", data}}}; spark::binary::Emit(context.CallInvoker(), response.Build(), delivered); } else delivered();
|
|
68
95
|
});
|
|
69
|
-
//
|
|
70
|
-
while (*active && wait.wait_for(std::chrono::milliseconds(100)) != std::future_status::ready) {}
|
|
96
|
+
// At most 1 MiB per stream awaits JS delivery; no per-chunk round trip.
|
|
71
97
|
}
|
|
72
98
|
}
|
|
73
99
|
}
|
|
@@ -76,8 +102,12 @@ struct Process : std::enable_shared_from_this<Process> {
|
|
|
76
102
|
if (!CreatePipe(read.put(), write.put(), &security, 0)) throw_last_error();
|
|
77
103
|
if (!SetHandleInformation(parentReads ? read.get() : write.get(), HANDLE_FLAG_INHERIT, 0)) throw_last_error();
|
|
78
104
|
}
|
|
79
|
-
void Start(Json::JsonObject const &args) {
|
|
105
|
+
void Start(Json::JsonObject const &args, std::optional<std::vector<uint8_t>> inputBytes) {
|
|
106
|
+
const auto limit = args.GetNamedNumber(L"captureLimitBytes", 8 * 1024 * 1024);
|
|
107
|
+
if (limit < 0 || limit > 8 * 1024 * 1024) throw hresult_invalid_argument(L"Invalid capture limit");
|
|
108
|
+
captureLimit = static_cast<size_t>(limit);
|
|
80
109
|
auto executable = std::wstring(args.GetNamedString(L"executable"));
|
|
110
|
+
if (args.GetNamedBoolean(L"command", false)) { executable = ResolveCommand(executable); if (executable.empty()) throw hresult_error(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), L"Command not found"); }
|
|
81
111
|
if (executable.rfind(L"helper:", 0) == 0) {
|
|
82
112
|
const auto name = executable.substr(7); if (name.empty() || name.find_first_not_of(L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-") != std::wstring::npos) throw hresult_invalid_argument(L"Invalid helper name");
|
|
83
113
|
wchar_t module[32768]{}; GetModuleFileNameW(nullptr, module, 32768);
|
|
@@ -117,7 +147,7 @@ struct Process : std::enable_shared_from_this<Process> {
|
|
|
117
147
|
process.attach(info.hProcess); handle thread{info.hThread};
|
|
118
148
|
if (!AssignProcessToJobObject(job.get(), process.get())) { auto error = GetLastError(); TerminateProcess(process.get(), 1); throw hresult_error(HRESULT_FROM_WIN32(error)); }
|
|
119
149
|
if (ResumeThread(thread.get()) == static_cast<DWORD>(-1)) { Stop(); throw_last_error(); }
|
|
120
|
-
if (
|
|
150
|
+
if (inputBytes) Write({std::move(*inputBytes), false, std::nullopt});
|
|
121
151
|
}
|
|
122
152
|
};
|
|
123
153
|
REACT_MODULE(SparkProcesses, L"NativeDesktopProcesses")
|
|
@@ -126,16 +156,30 @@ struct SparkProcesses {
|
|
|
126
156
|
std::shared_ptr<State> state = std::make_shared<State>();
|
|
127
157
|
REACT_INIT(Initialize) void Initialize(React::ReactContext const &context) noexcept { state->context = context; }
|
|
128
158
|
~SparkProcesses() { auto current = state; *current->active = false; if (current->context) current->context.UIDispatcher().Post([current]() { for (auto const &[id, process] : current->processes) process->Stop(); }); }
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
159
|
+
REACT_SYNC_METHOD(installBinary) std::optional<std::string> installBinary() noexcept {
|
|
160
|
+
try {
|
|
161
|
+
spark::binary::Install(state->context, "__sparkProcessesBinary", [current = state](std::string method, std::string encoded, std::optional<std::vector<uint8_t>> bytes, spark::binary::Completion finish) { Call(current, std::move(method), std::move(encoded), spark::binary::NativePromise(finish), std::move(bytes)); });
|
|
162
|
+
return std::nullopt;
|
|
163
|
+
} catch (std::exception const &error) { return error.what(); }
|
|
164
|
+
catch (hresult_error const &error) { return to_string(error.message()); }
|
|
165
|
+
catch (...) { return "Could not install native binary transport"; }
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
REACT_METHOD(call) void call(std::string method, std::string encoded, React::ReactPromise<std::string> promise) noexcept { Call(state, std::move(method), std::move(encoded), promise); }
|
|
169
|
+
static void Call(std::shared_ptr<State> current, std::string method, std::string encoded, spark::binary::NativePromise promise, std::optional<std::vector<uint8_t>> bytes = {}) {
|
|
170
|
+
current->context.UIDispatcher().Post([current, method = std::move(method), encoded = std::move(encoded), promise, bytes = std::move(bytes)]() mutable {
|
|
132
171
|
try {
|
|
133
172
|
if (!*current->active) { promise.Reject(React::ReactError{"E_CLOSED", "Process module is closed"}); return; }
|
|
134
|
-
auto args = Json::JsonObject::Parse(to_hstring(encoded));
|
|
173
|
+
auto args = Json::JsonObject::Parse(to_hstring(encoded));
|
|
174
|
+
if (method == "resolveCommand") {
|
|
175
|
+
auto path = ResolveCommand(std::wstring(args.GetNamedString(L"command")));
|
|
176
|
+
promise.Resolve(to_string(path.empty() ? Json::JsonValue::CreateNullValue().Stringify() : Json::JsonValue::CreateStringValue(path).Stringify())); return;
|
|
177
|
+
}
|
|
178
|
+
auto id = to_string(args.GetNamedString(L"id"));
|
|
135
179
|
auto found = current->processes.find(id);
|
|
136
180
|
if (method == "spawn") {
|
|
137
181
|
if (found != current->processes.end()) { promise.Reject(React::ReactError{"E_EXISTS", "Process id already exists"}); return; }
|
|
138
|
-
auto child = std::make_shared<Process>(); child->context = current->context; child->id = id; child->Start(args);
|
|
182
|
+
auto child = std::make_shared<Process>(); child->context = current->context; child->id = id; child->Start(args, std::move(bytes));
|
|
139
183
|
const auto timeout = args.HasKey(L"timeoutMs") ? static_cast<DWORD>(std::clamp(args.GetNamedNumber(L"timeoutMs"), 1.0, static_cast<double>(INFINITE - 1))) : INFINITE;
|
|
140
184
|
const bool stream = args.GetNamedBoolean(L"streamOutput", false);
|
|
141
185
|
std::thread([current, child, timeout, stream]() {
|
|
@@ -151,20 +195,25 @@ struct SparkProcesses {
|
|
|
151
195
|
TerminateJobObject(child->job.get(), 1); child->done = true; child->condition.notify_all();
|
|
152
196
|
if (input.joinable()) input.join(); if (output.joinable()) output.join(); if (error.joinable()) error.join();
|
|
153
197
|
current->context.UIDispatcher().Post([current, child, exit]() {
|
|
154
|
-
if (*current->active)
|
|
155
|
-
|
|
156
|
-
|
|
198
|
+
if (*current->active) {
|
|
199
|
+
Json::JsonObject result; result.SetNamedValue(L"exitCode", Json::JsonValue::CreateNumberValue(exit)); result.SetNamedValue(L"terminated", Json::JsonValue::CreateBooleanValue(child->terminated)); result.SetNamedValue(L"terminationSignal", Json::JsonValue::CreateNullValue()); result.SetNamedValue(L"timedOut", Json::JsonValue::CreateBooleanValue(child->timedOut)); result.SetNamedValue(L"outputTruncated", Json::JsonValue::CreateBooleanValue(child->truncated));
|
|
200
|
+
Json::JsonObject event; event.SetNamedValue(L"type", Json::JsonValue::CreateStringValue(L"processExit")); event.SetNamedValue(L"processId", Json::JsonValue::CreateStringValue(to_hstring(child->id))); event.SetNamedValue(L"result", result);
|
|
201
|
+
auto stdoutBytes = std::make_shared<CapturedBytes>(std::move(child->stdoutBytes)); auto stderrBytes = std::make_shared<CapturedBytes>(std::move(child->stderrBytes));
|
|
202
|
+
auto metadata = to_string(event.Stringify()); spark::binary::Emit(current->context.CallInvoker(), [metadata = std::move(metadata), stdoutBytes, stderrBytes](spark::binary::jsi::Runtime &rt) { auto value = spark::binary::JSON(metadata)(rt).asObject(rt); auto result = value.getPropertyAsObject(rt, "result"); result.setProperty(rt, "stdout", spark::binary::Buffer(rt, stdoutBytes)); result.setProperty(rt, "stderr", spark::binary::Buffer(rt, stderrBytes)); return spark::binary::jsi::Value(std::move(value)); });
|
|
203
|
+
}
|
|
157
204
|
current->processes.erase(child->id);
|
|
158
205
|
});
|
|
159
206
|
}).detach();
|
|
160
207
|
current->processes.emplace(id, child);
|
|
161
208
|
} else if (found == current->processes.end() || found->second->done) { if (method == "terminate" || method == "closeInput") promise.Resolve("null"); else promise.Reject(React::ReactError{"E_CLOSED", "Process has exited"}); return; }
|
|
162
|
-
else if (method == "terminate") found->second->
|
|
163
|
-
else if (method == "write" || method == "closeInput") { found->second->Write({method == "write" ?
|
|
209
|
+
else if (method == "terminate") found->second->StopChecked();
|
|
210
|
+
else if (method == "write" || method == "closeInput") { found->second->Write({method == "write" && bytes ? std::move(*bytes) : std::vector<uint8_t>{}, method == "closeInput", promise}); return; }
|
|
164
211
|
else throw hresult_invalid_argument(L"Unknown process operation");
|
|
165
212
|
promise.Resolve("null");
|
|
166
|
-
} catch (hresult_error const &error) {
|
|
167
|
-
|
|
213
|
+
} catch (hresult_error const &error) { const auto code = error.code();
|
|
214
|
+
const char *kind = code == E_INVALIDARG ? "E_INVALID_ARGUMENT" : code == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) || code == HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) ? "E_NOT_FOUND" : code == E_ACCESSDENIED ? "E_PERMISSION_DENIED" : "E_NATIVE";
|
|
215
|
+
promise.Reject(React::ReactError{kind, to_string(error.message())}); }
|
|
216
|
+
catch (std::exception const &error) { promise.Reject(React::ReactError{"E_NATIVE", error.what()}); }
|
|
168
217
|
});
|
|
169
218
|
}
|
|
170
219
|
};
|
package/vendor/node_modules/@legendapp/spark-processes/windows/SparkProcesses/SparkProcesses.vcxproj
CHANGED
|
@@ -80,6 +80,8 @@
|
|
|
80
80
|
<PreprocessorDefinitions>_WINRT_DLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
|
81
81
|
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
|
|
82
82
|
<AdditionalIncludeDirectories>
|
|
83
|
+
$(MSBuildThisFileDirectory)..\..\..\spark-desktop-app\cpp;
|
|
84
|
+
$(MSBuildThisFileDirectory)..\..\..\desktop-app\cpp;
|
|
83
85
|
$(MSBuildThisFileDirectory);
|
|
84
86
|
%(AdditionalIncludeDirectories)
|
|
85
87
|
</AdditionalIncludeDirectories>
|
|
@@ -1,50 +1,59 @@
|
|
|
1
1
|
#import "RNDesktopSecureStorage.h"
|
|
2
2
|
#import <RNDesktopApp/SparkDesktop.h>
|
|
3
3
|
#import <Security/Security.h>
|
|
4
|
+
|
|
5
|
+
static NSDictionary *Failure(NSString *code, NSString *message) {
|
|
6
|
+
return @{ @"error": @{ @"code": code, @"message": message } };
|
|
7
|
+
}
|
|
8
|
+
static NSDictionary *StorageOperation(NSString *method, NSDictionary *args) {
|
|
9
|
+
NSString *key = args[@"key"];
|
|
10
|
+
if (![key isKindOfClass:NSString.class] || !key.length || key.length > 200) return Failure(@"E_INVALID_ARGUMENT", @"Expected a Keychain key of 1–200 characters");
|
|
11
|
+
NSString *service = SparkNamespace();
|
|
12
|
+
NSMutableDictionary *query = [@{ (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
|
|
13
|
+
(__bridge id)kSecAttrService: service, (__bridge id)kSecAttrAccount: key } mutableCopy];
|
|
14
|
+
OSStatus status;
|
|
15
|
+
if ([method isEqual:@"get"]) {
|
|
16
|
+
query[(__bridge id)kSecReturnData] = @YES;
|
|
17
|
+
query[(__bridge id)kSecMatchLimit] = (__bridge id)kSecMatchLimitOne;
|
|
18
|
+
CFTypeRef result = NULL;
|
|
19
|
+
status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);
|
|
20
|
+
if (status == errSecItemNotFound) return @{ @"value": NSNull.null };
|
|
21
|
+
if (status == errSecSuccess) {
|
|
22
|
+
NSData *data = CFBridgingRelease(result);
|
|
23
|
+
NSString *value = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
|
24
|
+
return value ? @{ @"value": value } : Failure(@"E_KEYCHAIN", @"Keychain item is not valid UTF-8");
|
|
25
|
+
}
|
|
26
|
+
} else if ([method isEqual:@"set"]) {
|
|
27
|
+
if (![args[@"value"] isKindOfClass:NSString.class]) return Failure(@"E_INVALID_ARGUMENT", @"Expected a string value");
|
|
28
|
+
NSData *value = [args[@"value"] dataUsingEncoding:NSUTF8StringEncoding];
|
|
29
|
+
NSDictionary *attributes = @{ (__bridge id)kSecValueData: value };
|
|
30
|
+
status = SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)attributes);
|
|
31
|
+
if (status == errSecItemNotFound) {
|
|
32
|
+
[query addEntriesFromDictionary:attributes];
|
|
33
|
+
query[(__bridge id)kSecAttrAccessible] = (__bridge id)kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly;
|
|
34
|
+
status = SecItemAdd((__bridge CFDictionaryRef)query, NULL);
|
|
35
|
+
if (status == errSecDuplicateItem) {
|
|
36
|
+
[query removeObjectForKey:(__bridge id)kSecValueData];
|
|
37
|
+
[query removeObjectForKey:(__bridge id)kSecAttrAccessible];
|
|
38
|
+
status = SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)attributes);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
} else if ([method isEqual:@"remove"]) {
|
|
42
|
+
status = SecItemDelete((__bridge CFDictionaryRef)query);
|
|
43
|
+
if (status == errSecItemNotFound) status = errSecSuccess;
|
|
44
|
+
} else return Failure(@"E_INVALID_ARGUMENT", @"Unknown Keychain operation");
|
|
45
|
+
if (status == errSecSuccess) return @{ @"value": NSNull.null };
|
|
46
|
+
NSString *message = CFBridgingRelease(SecCopyErrorMessageString(status, NULL));
|
|
47
|
+
return Failure(@"E_KEYCHAIN", [NSString stringWithFormat:@"Keychain error %d: %@", (int)status, message ?: @"Unknown error"]);
|
|
48
|
+
}
|
|
4
49
|
@implementation RNDesktopSecureStorage
|
|
5
50
|
RCT_EXPORT_MODULE(NativeDesktopSecureStorage)
|
|
6
51
|
- (void)call:(NSString *)method args:(NSString *)json resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
|
|
7
52
|
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{
|
|
8
|
-
NSDictionary *
|
|
9
|
-
|
|
10
|
-
if (
|
|
11
|
-
|
|
12
|
-
NSMutableDictionary *query = [@{ (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
|
|
13
|
-
(__bridge id)kSecAttrService: SparkNamespace(), (__bridge id)kSecAttrAccount: key } mutableCopy];
|
|
14
|
-
OSStatus status;
|
|
15
|
-
if ([method isEqual:@"get"]) {
|
|
16
|
-
query[(__bridge id)kSecReturnData] = @YES;
|
|
17
|
-
query[(__bridge id)kSecMatchLimit] = (__bridge id)kSecMatchLimitOne;
|
|
18
|
-
CFTypeRef value = NULL;
|
|
19
|
-
status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &value);
|
|
20
|
-
if (status == errSecItemNotFound) { resolve(@"null"); return; }
|
|
21
|
-
NSData *data = CFBridgingRelease(value);
|
|
22
|
-
if (status == errSecSuccess) {
|
|
23
|
-
NSString *text = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
|
24
|
-
if (!text) { reject(@"E_ENCODING", @"Keychain value is not UTF-8", nil); return; }
|
|
25
|
-
resolve(SparkJSON(text)); return;
|
|
26
|
-
}
|
|
27
|
-
} else if ([method isEqual:@"set"]) {
|
|
28
|
-
if (![args[@"value"] isKindOfClass:NSString.class]) { SparkInvalid(reject, @"Expected a string value"); return; }
|
|
29
|
-
NSData *value = [args[@"value"] dataUsingEncoding:NSUTF8StringEncoding];
|
|
30
|
-
NSDictionary *attributes = @{ (__bridge id)kSecValueData: value };
|
|
31
|
-
status = SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)attributes);
|
|
32
|
-
if (status == errSecItemNotFound) {
|
|
33
|
-
[query addEntriesFromDictionary:attributes];
|
|
34
|
-
query[(__bridge id)kSecAttrAccessible] = (__bridge id)kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly;
|
|
35
|
-
status = SecItemAdd((__bridge CFDictionaryRef)query, NULL);
|
|
36
|
-
// Another caller may have added it after our update lookup.
|
|
37
|
-
if (status == errSecDuplicateItem) { [query removeObjectForKey:(__bridge id)kSecValueData]; [query removeObjectForKey:(__bridge id)kSecAttrAccessible]; status = SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)attributes); }
|
|
38
|
-
}
|
|
39
|
-
} else if ([method isEqual:@"remove"]) {
|
|
40
|
-
status = SecItemDelete((__bridge CFDictionaryRef)query);
|
|
41
|
-
if (status == errSecItemNotFound) status = errSecSuccess;
|
|
42
|
-
} else { SparkInvalid(reject, @"Unknown Keychain operation"); return; }
|
|
43
|
-
if (status == errSecSuccess) resolve(@"null");
|
|
44
|
-
else {
|
|
45
|
-
NSString *message = CFBridgingRelease(SecCopyErrorMessageString(status, NULL));
|
|
46
|
-
reject(@"E_KEYCHAIN", [NSString stringWithFormat:@"Keychain error %d: %@", (int)status, message ?: @"Unknown error"], nil);
|
|
47
|
-
}
|
|
53
|
+
NSDictionary *result = StorageOperation(method, SparkArgs(json));
|
|
54
|
+
NSDictionary *error = result[@"error"];
|
|
55
|
+
if (error) reject(error[@"code"], error[@"message"], nil);
|
|
56
|
+
else resolve(SparkJSON(result[@"value"]));
|
|
48
57
|
});
|
|
49
58
|
}
|
|
50
59
|
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const facebook::react::ObjCTurboModule::InitParams &)params {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@legendapp/spark-secure-storage",
|
|
3
|
-
"version": "0.0.1-next.
|
|
3
|
+
"version": "0.0.1-next.3",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"main": "src/index",
|
|
6
6
|
"react-native": "src/index",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
]
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@legendapp/spark-desktop-app": "0.0.1-next.
|
|
44
|
+
"@legendapp/spark-desktop-app": "0.0.1-next.3"
|
|
45
45
|
},
|
|
46
46
|
"peerDependenciesMeta": {
|
|
47
47
|
"expo-secure-store": {
|
package/vendor/node_modules/@legendapp/spark-secure-storage/src/NativeDesktopSecureStorage.ts
CHANGED
|
@@ -3,4 +3,4 @@ import { TurboModuleRegistry } from "react-native";
|
|
|
3
3
|
export interface Spec extends TurboModule {
|
|
4
4
|
call(method: string, args: string): Promise<string>;
|
|
5
5
|
}
|
|
6
|
-
export default TurboModuleRegistry.
|
|
6
|
+
export default TurboModuleRegistry.get<Spec>("NativeDesktopSecureStorage");
|
|
@@ -1,24 +1,26 @@
|
|
|
1
|
+
import { Platform } from "react-native";
|
|
2
|
+
import { SparkError, parseNativeResult, invokeNative } from "@legendapp/spark-desktop-app/src/contracts";
|
|
1
3
|
import Native from "./NativeDesktopSecureStorage";
|
|
2
|
-
function key(value: string) { if (!value.length || value.length > 200) throw new Error("Keychain keys must contain 1–200 characters"); return value; }
|
|
3
|
-
async function call<T = void>(method: string, args: object): Promise<T> { return JSON.parse(await Native.call(method, JSON.stringify(args))) as T; }
|
|
4
|
-
export const secureStorage = {
|
|
5
|
-
get: (name: string) => call<string | null>("get", { key: key(name) }),
|
|
6
|
-
set: (name: string, value: string) => call("set", { key: key(name), value }),
|
|
7
|
-
remove: (name: string) => call("remove", { key: key(name) }),
|
|
8
|
-
};
|
|
9
|
-
|
|
10
4
|
import { validateItem, type SecureStoreOptions } from "./options";
|
|
11
5
|
export type { SecureStoreOptions } from "./options";
|
|
12
|
-
|
|
6
|
+
function native() {
|
|
7
|
+
if (Platform.OS !== "macos" && Platform.OS !== "windows") throw new SparkError("E_UNSUPPORTED_PLATFORM", "Desktop secure storage is unavailable on this target");
|
|
8
|
+
if (!Native) throw new SparkError("E_MODULE_UNAVAILABLE", "NativeDesktopSecureStorage is not installed");
|
|
9
|
+
return Native;
|
|
10
|
+
}
|
|
11
|
+
async function call<T>(method: string, args: object, validate: (value: unknown) => value is T): Promise<T> {
|
|
12
|
+
return parseNativeResult(await invokeNative(() => native().call(method, JSON.stringify(args))), validate);
|
|
13
|
+
}
|
|
14
|
+
/** Selected Expo subset; service identity is project-scoped. Missing is null, including on desktop. */
|
|
13
15
|
export async function getItemAsync(key: string, options: SecureStoreOptions = {}): Promise<string | null> {
|
|
14
|
-
validateItem(key, options); return
|
|
16
|
+
validateItem(key, options); return call("get", { key }, (value): value is string | null => value === null || typeof value === "string");
|
|
15
17
|
}
|
|
16
18
|
export async function setItemAsync(key: string, value: string, options: SecureStoreOptions = {}): Promise<void> {
|
|
17
19
|
validateItem(key, options);
|
|
18
|
-
if (typeof value !== "string") throw new
|
|
19
|
-
await
|
|
20
|
+
if (typeof value !== "string") throw new SparkError("E_INVALID_ARGUMENT", "SecureStore values must be strings");
|
|
21
|
+
await call("set", { key, value }, (value): value is null => value === null);
|
|
20
22
|
}
|
|
21
23
|
export async function deleteItemAsync(key: string, options: SecureStoreOptions = {}): Promise<void> {
|
|
22
|
-
validateItem(key, options); await
|
|
24
|
+
validateItem(key, options); await call("remove", { key }, (value): value is null => value === null);
|
|
23
25
|
}
|
|
24
|
-
export async function isAvailableAsync(): Promise<boolean> { return
|
|
26
|
+
export async function isAvailableAsync(): Promise<boolean> { return (Platform.OS === "macos" || Platform.OS === "windows") && !!Native; }
|
|
@@ -11,4 +11,3 @@ export async function setItemAsync(key: string, value: string, options: SecureSt
|
|
|
11
11
|
export async function deleteItemAsync(key: string, options: SecureStoreOptions = {}): Promise<void> {
|
|
12
12
|
validateItem(key, options); await SecureStore.deleteItemAsync(key);
|
|
13
13
|
}
|
|
14
|
-
export const secureStorage = { get: getItemAsync, set: setItemAsync, remove: deleteItemAsync };
|