@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
package/vendor/node_modules/@legendapp/spark-file-dialog/windows/SparkFileDialog/SparkFileDialog.h
CHANGED
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
#pragma comment(lib, "Shlwapi.lib")
|
|
8
8
|
#include <winrt/Windows.Data.Json.h>
|
|
9
9
|
#include <filesystem>
|
|
10
|
-
#include <fstream>
|
|
11
10
|
#include <vector>
|
|
12
11
|
#pragma comment(lib, "Ole32.lib")
|
|
13
12
|
namespace winrt::SparkFileDialog {
|
|
@@ -18,29 +17,6 @@ inline std::filesystem::path FilePath(std::string const &value) {
|
|
|
18
17
|
if (!result.is_absolute() || value.find('\0') != std::string::npos) throw hresult_invalid_argument(L"Expected an absolute file path");
|
|
19
18
|
return result;
|
|
20
19
|
}
|
|
21
|
-
inline std::string Read(std::string const &value) {
|
|
22
|
-
auto file = FilePath(value);
|
|
23
|
-
if (std::filesystem::file_size(file) > 8 * 1024 * 1024) throw hresult_invalid_argument(L"This text editor supports files up to 8 MB");
|
|
24
|
-
std::ifstream input(file, std::ios::binary);
|
|
25
|
-
if (!input) throw hresult_error(E_FAIL, L"Could not read file");
|
|
26
|
-
std::string text((std::istreambuf_iterator<char>(input)), std::istreambuf_iterator<char>());
|
|
27
|
-
if (input.bad()) throw hresult_error(E_FAIL, L"File read failed");
|
|
28
|
-
if (text.starts_with("\xef\xbb\xbf")) text.erase(0, 3);
|
|
29
|
-
// Reject non-UTF-8 input instead of silently destroying its encoding on save.
|
|
30
|
-
(void)to_hstring(text);
|
|
31
|
-
return text;
|
|
32
|
-
}
|
|
33
|
-
inline void Write(std::string const &value, std::string const &contents) {
|
|
34
|
-
auto file = FilePath(value);
|
|
35
|
-
GUID guid{}; check_hresult(CoCreateGuid(&guid));
|
|
36
|
-
wchar_t id[40]{}; StringFromGUID2(guid, id, 40);
|
|
37
|
-
auto temporary = file; temporary += std::wstring(L".spark-") + id;
|
|
38
|
-
try {
|
|
39
|
-
{ std::ofstream output(temporary, std::ios::binary | std::ios::trunc);
|
|
40
|
-
output.exceptions(std::ios::badbit | std::ios::failbit); output.write(contents.data(), contents.size()); output.close(); }
|
|
41
|
-
if (!MoveFileExW(temporary.c_str(), file.c_str(), MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH)) throw_last_error();
|
|
42
|
-
} catch (...) { std::error_code ignored; std::filesystem::remove(temporary, ignored); throw; }
|
|
43
|
-
}
|
|
44
20
|
REACT_MODULE(SparkFileDialog, L"NativeFileDialog")
|
|
45
21
|
struct SparkFileDialog {
|
|
46
22
|
React::ReactContext context;
|
|
@@ -54,6 +30,18 @@ struct SparkFileDialog {
|
|
|
54
30
|
context.UIDispatcher().Post([save, options, promise]() {
|
|
55
31
|
try {
|
|
56
32
|
auto args = Json::JsonObject::Parse(to_hstring(options));
|
|
33
|
+
HWND parent = nullptr;
|
|
34
|
+
if (args.HasKey(L"windowId")) {
|
|
35
|
+
struct Search { std::wstring property; HWND found = nullptr; } search{L"Spark.Window." + std::wstring(args.GetNamedString(L"windowId"))};
|
|
36
|
+
EnumWindows([](HWND hwnd, LPARAM value) -> BOOL {
|
|
37
|
+
auto search = reinterpret_cast<Search *>(value); DWORD process = 0; GetWindowThreadProcessId(hwnd, &process);
|
|
38
|
+
if (process == GetCurrentProcessId() && GetPropW(hwnd, search->property.c_str())) { search->found = hwnd; return FALSE; }
|
|
39
|
+
return TRUE;
|
|
40
|
+
}, reinterpret_cast<LPARAM>(&search));
|
|
41
|
+
parent = search.found;
|
|
42
|
+
if (!parent) { promise.Reject(React::ReactError{"E_NOT_FOUND", "Dialog owner does not exist"}); return; }
|
|
43
|
+
if (!IsWindowEnabled(parent)) { promise.Reject(React::ReactError{"E_BUSY", "Dialog owner already has a modal operation"}); return; }
|
|
44
|
+
}
|
|
57
45
|
com_ptr<IFileDialog> dialog;
|
|
58
46
|
check_hresult(CoCreateInstance(save ? CLSID_FileSaveDialog : CLSID_FileOpenDialog, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(dialog.put())));
|
|
59
47
|
DWORD flags{}; check_hresult(dialog->GetOptions(&flags));
|
|
@@ -67,21 +55,43 @@ struct SparkFileDialog {
|
|
|
67
55
|
const auto message = args.GetNamedString(L"message", L"");
|
|
68
56
|
if (!message.empty()) { auto custom = dialog.as<IFileDialogCustomize>(); check_hresult(custom->AddText(100, message.c_str())); }
|
|
69
57
|
auto directory = args.GetNamedString(save ? L"directory" : L"directoryURL", L"");
|
|
58
|
+
std::wstring suggestedName = args.GetNamedString(L"defaultName", L"");
|
|
70
59
|
if (!directory.empty()) {
|
|
71
60
|
std::wstring value(directory);
|
|
72
61
|
if (value.rfind(L"file:", 0) == 0) { wchar_t buffer[32768]; DWORD length = 32768; check_hresult(PathCreateFromUrlW(value.c_str(), buffer, &length, 0)); value = buffer; }
|
|
62
|
+
// defaultPath may name a file for save dialogs: a live directory starts the
|
|
63
|
+
// panel; otherwise the last component suggests defaultName and the parent starts it.
|
|
64
|
+
if (save && GetFileAttributesW(value.c_str()) == INVALID_FILE_ATTRIBUTES) {
|
|
65
|
+
auto parsed = std::filesystem::path(value);
|
|
66
|
+
auto parent = parsed.parent_path();
|
|
67
|
+
if (suggestedName.empty()) suggestedName = parsed.filename().wstring();
|
|
68
|
+
if (!parent.empty()) value = parent.wstring();
|
|
69
|
+
}
|
|
73
70
|
auto folder = FilePath(to_string(value)); com_ptr<IShellItem> item;
|
|
74
71
|
check_hresult(SHCreateItemFromParsingName(folder.c_str(), nullptr, IID_PPV_ARGS(item.put())));
|
|
75
72
|
check_hresult(dialog->SetFolder(item.get()));
|
|
76
73
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
std::wstring
|
|
81
|
-
for (auto const &item :
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
74
|
+
if (save) check_hresult(dialog->SetFileName(suggestedName.empty() ? L"Untitled.txt" : suggestedName.c_str()));
|
|
75
|
+
// Windows presents one labeled filter group per filter, like Electron;
|
|
76
|
+
// hosts without per-filter labels read the flattened allowedFileTypes.
|
|
77
|
+
std::vector<std::wstring> labels, patterns;
|
|
78
|
+
if (args.HasKey(L"filters")) for (auto const &item : args.GetNamedArray(L"filters")) {
|
|
79
|
+
auto filter = item.GetObject();
|
|
80
|
+
std::wstring pattern;
|
|
81
|
+
for (auto const &extension : filter.GetNamedArray(L"extensions")) { if (!pattern.empty()) pattern += L";"; pattern += L"*." + std::wstring(extension.GetString()); }
|
|
82
|
+
if (pattern.empty()) continue;
|
|
83
|
+
labels.push_back(filter.HasKey(L"name") ? std::wstring(filter.GetNamedString(L"name")) : std::wstring(L"Supported files"));
|
|
84
|
+
patterns.push_back(pattern);
|
|
85
|
+
}
|
|
86
|
+
else if (args.HasKey(L"allowedFileTypes")) {
|
|
87
|
+
std::wstring pattern;
|
|
88
|
+
for (auto const &item : args.GetNamedArray(L"allowedFileTypes")) { if (!pattern.empty()) pattern += L";"; pattern += L"*." + std::wstring(item.GetString()); }
|
|
89
|
+
if (!pattern.empty()) { labels.push_back(L"Supported files"); patterns.push_back(pattern); }
|
|
90
|
+
}
|
|
91
|
+
std::vector<COMDLG_FILTERSPEC> filterSpecs;
|
|
92
|
+
for (size_t index = 0; index < patterns.size(); ++index) filterSpecs.push_back(COMDLG_FILTERSPEC{labels[index].c_str(), patterns[index].c_str()});
|
|
93
|
+
if (!filterSpecs.empty()) check_hresult(dialog->SetFileTypes(static_cast<UINT>(filterSpecs.size()), filterSpecs.data()));
|
|
94
|
+
auto status = dialog->Show(parent);
|
|
85
95
|
if (status == HRESULT_FROM_WIN32(ERROR_CANCELLED)) { promise.Resolve("null"); return; }
|
|
86
96
|
check_hresult(status);
|
|
87
97
|
auto pathFor = [](IShellItem *item) {
|
|
@@ -98,31 +108,5 @@ struct SparkFileDialog {
|
|
|
98
108
|
} catch (hresult_error const &error) { promise.Reject(React::ReactError{"E_DIALOG", to_string(error.message())}); }
|
|
99
109
|
});
|
|
100
110
|
}
|
|
101
|
-
REACT_METHOD(readTextFile)
|
|
102
|
-
void readTextFile(std::string file, React::ReactPromise<std::string> promise) noexcept {
|
|
103
|
-
try { promise.Resolve(Read(file)); } catch (std::exception const &error) { promise.Reject(error.what()); } catch (hresult_error const &error) { promise.Reject(to_string(error.message()).c_str()); }
|
|
104
|
-
}
|
|
105
|
-
REACT_METHOD(writeTextFile)
|
|
106
|
-
void writeTextFile(std::string file, std::string contents, React::ReactPromise<void> promise) noexcept {
|
|
107
|
-
try { Write(file, contents); promise.Resolve(); } catch (std::exception const &error) { promise.Reject(error.what()); } catch (hresult_error const &error) { promise.Reject(to_string(error.message()).c_str()); }
|
|
108
|
-
}
|
|
109
|
-
REACT_METHOD(writeTextFileIfUnchanged)
|
|
110
|
-
void writeTextFileIfUnchanged(std::string file, std::string expected, std::string contents, React::ReactPromise<bool> promise) noexcept {
|
|
111
|
-
try { if (Read(file) != expected) { promise.Resolve(false); return; } Write(file, contents); promise.Resolve(true); }
|
|
112
|
-
catch (std::exception const &error) { promise.Reject(error.what()); } catch (hresult_error const &error) { promise.Reject(to_string(error.message()).c_str()); }
|
|
113
|
-
}
|
|
114
|
-
REACT_METHOD(revealInFinder)
|
|
115
|
-
void revealInFinder(std::string file, React::ReactPromise<bool> promise) noexcept {
|
|
116
|
-
context.UIDispatcher().Post([file, promise] {
|
|
117
|
-
PIDLIST_ABSOLUTE item = nullptr;
|
|
118
|
-
try {
|
|
119
|
-
auto path = FilePath(file);
|
|
120
|
-
check_hresult(SHParseDisplayName(path.c_str(), nullptr, &item, 0, nullptr));
|
|
121
|
-
auto result = SHOpenFolderAndSelectItems(item, 0, nullptr, 0);
|
|
122
|
-
CoTaskMemFree(item); item = nullptr; check_hresult(result); promise.Resolve(true);
|
|
123
|
-
} catch (hresult_error const &error) { if (item) CoTaskMemFree(item); promise.Reject(React::ReactError{"E_REVEAL", to_string(error.message())}); }
|
|
124
|
-
catch (std::exception const &error) { if (item) CoTaskMemFree(item); promise.Reject(error.what()); }
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
111
|
};
|
|
128
112
|
}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
#import "RNDesktopFileSystem.h"
|
|
2
|
+
#import <RNDesktopApp/SparkBinaryJSI.h>
|
|
3
|
+
#import <AppKit/AppKit.h>
|
|
4
|
+
#import "SparkFileMutations.h"
|
|
2
5
|
#import <RNDesktopApp/SparkDesktop.h>
|
|
3
6
|
#import <fcntl.h>
|
|
4
7
|
#import <unistd.h>
|
|
@@ -51,13 +54,15 @@ RCT_EXPORT_MODULE(NativeDesktopFileSystem)
|
|
|
51
54
|
+ (BOOL)requiresMainQueueSetup { return NO; }
|
|
52
55
|
- (instancetype)init { if (self = [super init]) { _ioQueue = dispatch_queue_create("spark.files", DISPATCH_QUEUE_SERIAL); _files = [NSMutableDictionary new]; _watches = [NSMutableDictionary new]; _recursiveWatches = [NSMutableDictionary new]; } return self; }
|
|
53
56
|
- (NSArray<NSString *> *)supportedEvents { return @[@"change"]; }
|
|
54
|
-
- (void)call:(NSString *)method args:(NSString *)json resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
|
|
57
|
+
- (void)call:(NSString *)method args:(NSString *)json resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { [self perform:method args:SparkArgs(json) binary:NO resolve:resolve reject:reject]; }
|
|
58
|
+
- (void)binaryCall:(NSString *)method args:(NSString *)json bytes:(NSData *)bytes resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { NSMutableDictionary *args = [SparkArgs(json) mutableCopy]; if (bytes) args[@"bytes"] = bytes; [self perform:method args:args binary:YES resolve:resolve reject:reject]; }
|
|
59
|
+
- (void)perform:(NSString *)method args:(NSDictionary *)args binary:(BOOL)binary resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
|
|
55
60
|
dispatch_async(self.ioQueue, ^{
|
|
56
|
-
|
|
61
|
+
NSFileManager *fm = NSFileManager.defaultManager;
|
|
57
62
|
NSError *error = nil; id result = NSNull.null;
|
|
58
63
|
if ([@[@"readChunk", @"writeChunk", @"flushFile", @"closeFile"] containsObject:method]) {
|
|
59
64
|
NSString *identifier = args[@"id"]; SparkOpenFile *file = self.files[identifier];
|
|
60
|
-
if ([method isEqual:@"closeFile"]) { [self.files removeObjectForKey:identifier]; resolve(@"null"); return; }
|
|
65
|
+
if ([method isEqual:@"closeFile"]) { [self.files removeObjectForKey:identifier]; resolve(binary ? NSNull.null : @"null"); return; }
|
|
61
66
|
if (!file) { reject(@"E_CLOSED", @"Unknown or closed file handle", nil); return; }
|
|
62
67
|
if ([method isEqual:@"flushFile"]) { if (fsync(file.descriptor)) error = [NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:nil]; }
|
|
63
68
|
else {
|
|
@@ -69,11 +74,9 @@ RCT_EXPORT_MODULE(NativeDesktopFileSystem)
|
|
|
69
74
|
NSMutableData *data = [NSMutableData dataWithLength:(NSUInteger)size]; ssize_t count;
|
|
70
75
|
do { count = pread(file.descriptor, data.mutableBytes, data.length, (off_t)position); } while (count < 0 && errno == EINTR);
|
|
71
76
|
if (count < 0) error = [NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:nil];
|
|
72
|
-
else { data.length = count; result =
|
|
77
|
+
else { data.length = count; result = data; }
|
|
73
78
|
} else {
|
|
74
|
-
|
|
75
|
-
if (![base64 isKindOfClass:NSString.class] || base64.length > 1398104) { SparkInvalid(reject, @"Chunk exceeds 1 MiB"); return; }
|
|
76
|
-
NSData *data = [[NSData alloc] initWithBase64EncodedString:base64 options:0];
|
|
79
|
+
NSData *data = args[@"bytes"];
|
|
77
80
|
if (!data || data.length > 1048576 || position + data.length > 9007199254740991.0) { SparkInvalid(reject, @"Invalid chunk"); return; }
|
|
78
81
|
NSUInteger written = 0;
|
|
79
82
|
while (written < data.length) {
|
|
@@ -122,29 +125,37 @@ RCT_EXPORT_MODULE(NativeDesktopFileSystem)
|
|
|
122
125
|
} else if ([method isEqual:@"trash"]) { [fm trashItemAtURL:url resultingItemURL:nil error:&error]; }
|
|
123
126
|
else if ([method isEqual:@"readText"]) result = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&error];
|
|
124
127
|
else if ([method isEqual:@"readBytes"]) {
|
|
125
|
-
|
|
126
|
-
result =
|
|
128
|
+
NSMutableData *data = [NSMutableData dataWithContentsOfURL:url options:0 error:&error];
|
|
129
|
+
result = data;
|
|
130
|
+
}
|
|
131
|
+
else if ([method isEqual:@"reveal"]) {
|
|
132
|
+
if (![fm attributesOfItemAtPath:url.path error:&error]) { SparkReject(reject, error); return; }
|
|
133
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
134
|
+
[[NSWorkspace sharedWorkspace] activateFileViewerSelectingURLs:@[url]];
|
|
135
|
+
resolve(binary ? NSNull.null : @"null");
|
|
136
|
+
});
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
else if ([method isEqual:@"writeTextIfUnchanged"]) {
|
|
140
|
+
result = @(SparkWriteTextIfUnchanged(url, args[@"expected"], args[@"text"], &error));
|
|
127
141
|
}
|
|
128
142
|
else if ([method isEqual:@"writeText"]) [args[@"text"] writeToURL:url atomically:YES encoding:NSUTF8StringEncoding error:&error];
|
|
129
143
|
else if ([method isEqual:@"writeBytes"]) {
|
|
130
|
-
NSData *data =
|
|
131
|
-
if (!data) { SparkInvalid(reject, @"
|
|
144
|
+
NSData *data = args[@"bytes"];
|
|
145
|
+
if (!data) { SparkInvalid(reject, @"Expected binary file data"); return; }
|
|
132
146
|
[data writeToURL:url options:NSDataWritingAtomic error:&error];
|
|
133
147
|
}
|
|
134
148
|
else if ([method isEqual:@"mkdir"]) [fm createDirectoryAtURL:url withIntermediateDirectories:[args[@"recursive"] boolValue] attributes:nil error:&error];
|
|
135
149
|
else if ([method isEqual:@"remove"]) {
|
|
136
|
-
|
|
137
|
-
if (
|
|
138
|
-
|
|
139
|
-
reject(@"E_NOT_EMPTY", @"Directory is not empty; pass recursive: true", nil); return;
|
|
150
|
+
result = @(SparkRemoveFile(url, [args[@"recursive"] boolValue], &error));
|
|
151
|
+
if ([error.domain isEqual:NSPOSIXErrorDomain] && error.code == ENOTEMPTY) {
|
|
152
|
+
reject(@"E_NOT_EMPTY", @"Directory is not empty; pass recursive: true", error); return;
|
|
140
153
|
}
|
|
141
|
-
if (!error) result = @([fm removeItemAtURL:url error:&error]);
|
|
142
154
|
}
|
|
143
155
|
else if ([method isEqual:@"copy"] || [method isEqual:@"move"]) {
|
|
144
156
|
NSURL *to = FileURL(args[@"to"]);
|
|
145
157
|
if (!to || !to.isFileURL || (to.host.length && ![to.host isEqual:@"localhost"])) { SparkInvalid(reject, @"Expected an absolute destination"); return; }
|
|
146
|
-
|
|
147
|
-
else [fm moveItemAtURL:url toURL:to error:&error];
|
|
158
|
+
SparkTransferPath(url, to, [method isEqual:@"copy"] ? SparkTransferCopy : SparkTransferMove, [args[@"overwrite"] boolValue], &error);
|
|
148
159
|
}
|
|
149
160
|
else if ([method isEqual:@"stat"]) {
|
|
150
161
|
NSDictionary *attrs = [fm attributesOfItemAtPath:url.path error:&error];
|
|
@@ -177,7 +188,7 @@ RCT_EXPORT_MODULE(NativeDesktopFileSystem)
|
|
|
177
188
|
if (!watch.stream) { reject(@"E_WATCH", @"Could not create directory watcher", nil); return; }
|
|
178
189
|
FSEventStreamSetDispatchQueue(watch.stream, self.ioQueue);
|
|
179
190
|
if (!FSEventStreamStart(watch.stream)) { [watch stop]; reject(@"E_WATCH", @"Could not start directory watcher", nil); return; }
|
|
180
|
-
self.recursiveWatches[watchID] = watch; resolve(@"null"); return;
|
|
191
|
+
self.recursiveWatches[watchID] = watch; resolve(binary ? NSNull.null : @"null"); return;
|
|
181
192
|
}
|
|
182
193
|
// Observe the parent so replacing a file atomically does not lose its watch.
|
|
183
194
|
BOOL isDirectory = NO;
|
|
@@ -198,7 +209,7 @@ RCT_EXPORT_MODULE(NativeDesktopFileSystem)
|
|
|
198
209
|
}
|
|
199
210
|
else { SparkInvalid(reject, @"Unknown filesystem operation"); return; }
|
|
200
211
|
}
|
|
201
|
-
if (error) SparkReject(reject, error); else resolve(SparkJSON(result));
|
|
212
|
+
if (error) SparkReject(reject, error); else resolve(binary ? result : SparkJSON(result));
|
|
202
213
|
});
|
|
203
214
|
}
|
|
204
215
|
- (void)invalidate {
|
|
@@ -206,6 +217,6 @@ RCT_EXPORT_MODULE(NativeDesktopFileSystem)
|
|
|
206
217
|
[super invalidate];
|
|
207
218
|
}
|
|
208
219
|
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const facebook::react::ObjCTurboModule::InitParams &)params {
|
|
209
|
-
return std::make_shared<facebook::react::NativeDesktopFileSystemSpecJSI
|
|
220
|
+
return std::make_shared<spark::binary::Module<facebook::react::NativeDesktopFileSystemSpecJSI>>(params);
|
|
210
221
|
}
|
|
211
222
|
@end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#import <Foundation/Foundation.h>
|
|
2
|
+
|
|
3
|
+
enum SparkTransferKind {
|
|
4
|
+
SparkTransferCopy,
|
|
5
|
+
SparkTransferMove,
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
// A false result with no error means conflict/absence respectively.
|
|
9
|
+
BOOL SparkWriteTextIfUnchanged(NSURL *url, NSString *expected, NSString *text, NSError **error);
|
|
10
|
+
BOOL SparkRemoveFile(NSURL *url, BOOL recursive, NSError **error);
|
|
11
|
+
#if defined(SPARK_FILE_MUTATIONS_TESTING)
|
|
12
|
+
enum SparkTransferTestFailure {
|
|
13
|
+
SparkFailStageCopy = 1 << 0,
|
|
14
|
+
SparkFailPublish = 1 << 1,
|
|
15
|
+
SparkFailRestoreDestination = 1 << 2,
|
|
16
|
+
SparkFailRestoreSource = 1 << 3,
|
|
17
|
+
SparkFailSourceRemoval = 1 << 4,
|
|
18
|
+
SparkFailBackupRemoval = 1 << 5,
|
|
19
|
+
SparkFailStageCleanup = 1 << 6,
|
|
20
|
+
};
|
|
21
|
+
void SparkSetTransferTestFailures(unsigned failures, BOOL forceCrossVolumeMove);
|
|
22
|
+
#endif
|
|
23
|
+
/** Copies or moves, replacing the destination only when overwrite is set. An existing destination otherwise conflicts with NSFileWriteFileExistsError, including a dangling link. Self-transfers and overlapping paths reject. Transfers stage beside the destination before publication; cross-volume moves copy then delete. A cross-volume source-cleanup failure can leave a partial source; source-cleanup and backup-cleanup errors report committed and retained paths. */
|
|
24
|
+
BOOL SparkTransferPath(NSURL *from, NSURL *to, SparkTransferKind kind, BOOL overwrite, NSError **error);
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
#import "SparkFileMutations.h"
|
|
2
|
+
#import <sys/stat.h>
|
|
3
|
+
#import <errno.h>
|
|
4
|
+
#import <unistd.h>
|
|
5
|
+
#import <limits.h>
|
|
6
|
+
#import <stdio.h>
|
|
7
|
+
|
|
8
|
+
#if defined(SPARK_FILE_MUTATIONS_TESTING)
|
|
9
|
+
static unsigned SparkTransferTestFailures;
|
|
10
|
+
static BOOL SparkForceCrossVolumeMove;
|
|
11
|
+
void SparkSetTransferTestFailures(unsigned failures, BOOL forceCrossVolumeMove) {
|
|
12
|
+
SparkTransferTestFailures = failures;
|
|
13
|
+
SparkForceCrossVolumeMove = forceCrossVolumeMove;
|
|
14
|
+
}
|
|
15
|
+
static BOOL SparkTakeTransferTestFailure(unsigned failure) {
|
|
16
|
+
if (!(SparkTransferTestFailures & failure)) return NO;
|
|
17
|
+
SparkTransferTestFailures &= ~failure;
|
|
18
|
+
errno = EIO;
|
|
19
|
+
return YES;
|
|
20
|
+
}
|
|
21
|
+
#define SPARK_TAKE_FAILURE(failure) SparkTakeTransferTestFailure(failure)
|
|
22
|
+
#else
|
|
23
|
+
#define SPARK_TAKE_FAILURE(failure) NO
|
|
24
|
+
enum { SparkFailStageCopy = 0, SparkFailPublish = 0, SparkFailRestoreDestination = 0, SparkFailRestoreSource = 0,
|
|
25
|
+
SparkFailSourceRemoval = 0, SparkFailBackupRemoval = 0, SparkFailStageCleanup = 0 };
|
|
26
|
+
#endif
|
|
27
|
+
|
|
28
|
+
static int SparkPublishRename(NSString *from, NSString *to, unsigned failure) {
|
|
29
|
+
#if !defined(SPARK_FILE_MUTATIONS_TESTING)
|
|
30
|
+
(void)failure;
|
|
31
|
+
#endif
|
|
32
|
+
return SPARK_TAKE_FAILURE(failure) ? -1 : renamex_np(from.fileSystemRepresentation, to.fileSystemRepresentation, RENAME_EXCL);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
static NSString *SparkSiblingPath(NSURL *url, NSString *label) {
|
|
36
|
+
return [url.URLByDeletingLastPathComponent.path stringByAppendingPathComponent:
|
|
37
|
+
[NSString stringWithFormat:@".spark-%@-%@", label, NSUUID.UUID.UUIDString]];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
static NSString *SparkResolvedPath(NSURL *url) {
|
|
41
|
+
char resolved[PATH_MAX];
|
|
42
|
+
if (realpath(url.fileSystemRepresentation, resolved)) return @(resolved);
|
|
43
|
+
NSString *parent = url.URLByDeletingLastPathComponent.path;
|
|
44
|
+
if (realpath(parent.fileSystemRepresentation, resolved))
|
|
45
|
+
return [[@(resolved) stringByAppendingPathComponent:url.lastPathComponent] stringByStandardizingPath];
|
|
46
|
+
return url.URLByStandardizingPath.path;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
static BOOL SparkPathContains(NSString *parent, NSString *child) {
|
|
50
|
+
if ([parent isEqualToString:child]) return YES;
|
|
51
|
+
NSString *prefix = [parent hasSuffix:@"/"] ? parent : [parent stringByAppendingString:@"/"];
|
|
52
|
+
return [child hasPrefix:prefix];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
static BOOL SparkRejectUnsafeRelationship(NSURL *from, NSURL *to, NSError **error) {
|
|
56
|
+
struct stat sourceInfo, destinationInfo;
|
|
57
|
+
BOOL sourceExists = lstat(from.fileSystemRepresentation, &sourceInfo) == 0;
|
|
58
|
+
BOOL destinationExists = lstat(to.fileSystemRepresentation, &destinationInfo) == 0;
|
|
59
|
+
BOOL sameInode = sourceExists && destinationExists && sourceInfo.st_dev == destinationInfo.st_dev && sourceInfo.st_ino == destinationInfo.st_ino;
|
|
60
|
+
NSString *sourcePath = SparkResolvedPath(from), *destinationPath = SparkResolvedPath(to);
|
|
61
|
+
BOOL overlap = SparkPathContains(sourcePath, destinationPath) || SparkPathContains(destinationPath, sourcePath);
|
|
62
|
+
if (!sameInode && !overlap) return NO;
|
|
63
|
+
if (error) *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileWriteInvalidFileNameError userInfo:@{NSLocalizedDescriptionKey: @"Source and destination identify or contain one another"}];
|
|
64
|
+
return YES;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
BOOL SparkWriteTextIfUnchanged(NSURL *url, NSString *expected, NSString *text, NSError **error) {
|
|
68
|
+
NSString *current = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:error];
|
|
69
|
+
if (!current || ![current isEqualToString:expected]) return NO;
|
|
70
|
+
return [text writeToURL:url atomically:YES encoding:NSUTF8StringEncoding error:error];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
BOOL SparkTransferPath(NSURL *from, NSURL *to, SparkTransferKind kind, BOOL overwrite, NSError **error) {
|
|
74
|
+
NSFileManager *fm = NSFileManager.defaultManager;
|
|
75
|
+
if (SparkRejectUnsafeRelationship(from, to, error)) return NO;
|
|
76
|
+
struct stat existing;
|
|
77
|
+
BOOL destinationExists = NO;
|
|
78
|
+
if (lstat(to.fileSystemRepresentation, &existing) == 0) {
|
|
79
|
+
destinationExists = YES;
|
|
80
|
+
if (!overwrite) {
|
|
81
|
+
if (error) *error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileWriteFileExistsError userInfo:nil];
|
|
82
|
+
return NO;
|
|
83
|
+
}
|
|
84
|
+
} else if (errno != ENOENT && errno != ENOTDIR) {
|
|
85
|
+
if (error) *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:nil];
|
|
86
|
+
return NO;
|
|
87
|
+
}
|
|
88
|
+
NSString *stagePath = SparkSiblingPath(to, @"stage");
|
|
89
|
+
NSURL *stage = [NSURL fileURLWithPath:stagePath];
|
|
90
|
+
struct stat sourceStat, parentStat;
|
|
91
|
+
BOOL sameVolumeMove = kind == SparkTransferMove && lstat(from.fileSystemRepresentation, &sourceStat) == 0 &&
|
|
92
|
+
stat(to.URLByDeletingLastPathComponent.fileSystemRepresentation, &parentStat) == 0 && sourceStat.st_dev == parentStat.st_dev;
|
|
93
|
+
#if defined(SPARK_FILE_MUTATIONS_TESTING)
|
|
94
|
+
if (SparkForceCrossVolumeMove) sameVolumeMove = NO;
|
|
95
|
+
#endif
|
|
96
|
+
BOOL stagedByRename = NO;
|
|
97
|
+
if (sameVolumeMove) {
|
|
98
|
+
if (rename(from.fileSystemRepresentation, stage.fileSystemRepresentation) != 0) {
|
|
99
|
+
int code = errno;
|
|
100
|
+
if (error) *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:code userInfo:nil];
|
|
101
|
+
return NO;
|
|
102
|
+
}
|
|
103
|
+
stagedByRename = YES;
|
|
104
|
+
} else {
|
|
105
|
+
NSError *copyError = nil;
|
|
106
|
+
BOOL staged = YES;
|
|
107
|
+
if (SPARK_TAKE_FAILURE(SparkFailStageCopy)) {
|
|
108
|
+
#if defined(SPARK_FILE_MUTATIONS_TESTING)
|
|
109
|
+
[@"partial stage" writeToURL:stage atomically:YES encoding:NSUTF8StringEncoding error:nil];
|
|
110
|
+
#endif
|
|
111
|
+
copyError = [NSError errorWithDomain:NSPOSIXErrorDomain code:EIO userInfo:nil];
|
|
112
|
+
staged = NO;
|
|
113
|
+
} else {
|
|
114
|
+
staged = [fm copyItemAtURL:from toURL:stage error:©Error];
|
|
115
|
+
}
|
|
116
|
+
if (!staged) {
|
|
117
|
+
NSMutableDictionary *info = [copyError.userInfo mutableCopy] ?: [NSMutableDictionary dictionary];
|
|
118
|
+
struct stat partial;
|
|
119
|
+
if (lstat(stage.fileSystemRepresentation, &partial) == 0) {
|
|
120
|
+
NSError *cleanupError = nil;
|
|
121
|
+
BOOL failCleanup = SPARK_TAKE_FAILURE(SparkFailStageCleanup);
|
|
122
|
+
if (failCleanup) cleanupError = [NSError errorWithDomain:NSPOSIXErrorDomain code:EIO userInfo:nil];
|
|
123
|
+
if (failCleanup || ![fm removeItemAtURL:stage error:&cleanupError]) {
|
|
124
|
+
info[@"SparkRecoverableStagePath"] = stagePath;
|
|
125
|
+
info[NSLocalizedDescriptionKey] = [NSString stringWithFormat:@"Transfer staging failed and partial stage cleanup failed; recoverable stage: %@", stagePath];
|
|
126
|
+
if (cleanupError) info[@"SparkStageCleanupError"] = cleanupError;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
if (error) *error = [NSError errorWithDomain:(copyError.domain ?: NSPOSIXErrorDomain)
|
|
130
|
+
code:(copyError ? copyError.code : EIO) userInfo:info];
|
|
131
|
+
return NO;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
NSString *backupPath = nil;
|
|
135
|
+
if (destinationExists) {
|
|
136
|
+
backupPath = SparkSiblingPath(to, @"backup");
|
|
137
|
+
if (rename(to.fileSystemRepresentation, backupPath.fileSystemRepresentation) != 0) {
|
|
138
|
+
int code = errno;
|
|
139
|
+
int rollbackCode = 0;
|
|
140
|
+
if (stagedByRename && SparkPublishRename(stagePath, from.path, SparkFailRestoreSource) != 0) rollbackCode = errno;
|
|
141
|
+
else if (!stagedByRename && ![fm removeItemAtURL:stage error:nil]) rollbackCode = errno ?: EIO;
|
|
142
|
+
if (error) *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:code userInfo:rollbackCode ? @{
|
|
143
|
+
NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Destination staging failed and source rollback failed (%d); recoverable source: %@", rollbackCode, stagePath],
|
|
144
|
+
NSUnderlyingErrorKey: [NSError errorWithDomain:NSPOSIXErrorDomain code:rollbackCode userInfo:nil],
|
|
145
|
+
@"SparkRecoverableStagePath": stagePath,
|
|
146
|
+
} : nil];
|
|
147
|
+
return NO;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
int publishResult = SparkPublishRename(stagePath, to.path, SparkFailPublish);
|
|
151
|
+
if (publishResult != 0) {
|
|
152
|
+
int code = errno;
|
|
153
|
+
int rollbackCode = 0;
|
|
154
|
+
if (backupPath && SparkPublishRename(backupPath, to.path, SparkFailRestoreDestination) != 0) rollbackCode = errno;
|
|
155
|
+
if (stagedByRename && SparkPublishRename(stagePath, from.path, SparkFailRestoreSource) != 0) rollbackCode = errno;
|
|
156
|
+
if (!stagedByRename && ![fm removeItemAtURL:stage error:nil]) rollbackCode = errno ?: EIO;
|
|
157
|
+
if (error) *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:code userInfo:rollbackCode ? @{
|
|
158
|
+
NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Transfer publication failed and rollback failed (%d); recoverable stage: %@; destination backup: %@", rollbackCode, stagePath, backupPath ?: @"none"],
|
|
159
|
+
NSUnderlyingErrorKey: [NSError errorWithDomain:NSPOSIXErrorDomain code:rollbackCode userInfo:nil],
|
|
160
|
+
@"SparkRecoverableStagePath": stagePath,
|
|
161
|
+
@"SparkRecoverableBackupPath": backupPath ?: @"",
|
|
162
|
+
} : nil];
|
|
163
|
+
return NO;
|
|
164
|
+
}
|
|
165
|
+
BOOL failSourceRemoval = SPARK_TAKE_FAILURE(SparkFailSourceRemoval);
|
|
166
|
+
if (kind == SparkTransferMove && !stagedByRename && (failSourceRemoval || ![fm removeItemAtURL:from error:error])) {
|
|
167
|
+
if (failSourceRemoval && error) *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:EIO userInfo:nil];
|
|
168
|
+
if (error && *error) {
|
|
169
|
+
NSMutableDictionary *info = [(*error).userInfo mutableCopy] ?: [NSMutableDictionary dictionary];
|
|
170
|
+
info[NSLocalizedDescriptionKey] = [NSString stringWithFormat:@"Source cleanup failed after publishing %@; source may be partially removed%@", to.path, backupPath ? [NSString stringWithFormat:@"; previous destination is retained at %@", backupPath] : @""];
|
|
171
|
+
info[@"SparkPublishedDestinationPath"] = to.path;
|
|
172
|
+
if (backupPath) info[@"SparkPreviousDestinationBackupPath"] = backupPath;
|
|
173
|
+
*error = [NSError errorWithDomain:(*error).domain code:(*error).code userInfo:info];
|
|
174
|
+
}
|
|
175
|
+
return NO;
|
|
176
|
+
}
|
|
177
|
+
if (backupPath) {
|
|
178
|
+
NSError *cleanupError = nil;
|
|
179
|
+
BOOL failCleanup = SPARK_TAKE_FAILURE(SparkFailBackupRemoval);
|
|
180
|
+
BOOL removed = !failCleanup && [fm removeItemAtPath:backupPath error:&cleanupError];
|
|
181
|
+
if (!removed) {
|
|
182
|
+
if (failCleanup) cleanupError = [NSError errorWithDomain:NSPOSIXErrorDomain code:EIO userInfo:nil];
|
|
183
|
+
if (error) *error = [NSError errorWithDomain:(cleanupError.domain ?: NSPOSIXErrorDomain) code:cleanupError.code userInfo:@{
|
|
184
|
+
NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Transfer committed at %@, but previous destination cleanup failed; recoverable backup: %@", to.path, backupPath],
|
|
185
|
+
NSUnderlyingErrorKey: cleanupError ?: [NSError errorWithDomain:NSPOSIXErrorDomain code:EIO userInfo:nil],
|
|
186
|
+
@"SparkPublishedDestinationPath": to.path,
|
|
187
|
+
@"SparkRecoverableBackupPath": backupPath,
|
|
188
|
+
}];
|
|
189
|
+
return NO;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return YES;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
BOOL SparkRemoveFile(NSURL *url, BOOL recursive, NSError **error) {
|
|
196
|
+
struct stat info;
|
|
197
|
+
if (lstat(url.fileSystemRepresentation, &info) != 0) {
|
|
198
|
+
int code = errno;
|
|
199
|
+
if (code == ENOENT || code == ENOTDIR) return NO;
|
|
200
|
+
if (error) *error = [NSError errorWithDomain:NSCocoaErrorDomain
|
|
201
|
+
code:(code == EACCES || code == EPERM) ? NSFileReadNoPermissionError : NSFileReadUnknownError
|
|
202
|
+
userInfo:@{NSUnderlyingErrorKey: [NSError errorWithDomain:NSPOSIXErrorDomain code:code userInfo:nil]}];
|
|
203
|
+
return NO;
|
|
204
|
+
}
|
|
205
|
+
NSFileManager *fm = NSFileManager.defaultManager;
|
|
206
|
+
if (S_ISDIR(info.st_mode) && !recursive) {
|
|
207
|
+
NSArray *children = [fm contentsOfDirectoryAtPath:url.path error:error];
|
|
208
|
+
if (!children) return NO;
|
|
209
|
+
if (children.count) {
|
|
210
|
+
if (error) *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:ENOTEMPTY userInfo:nil];
|
|
211
|
+
return NO;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return [fm removeItemAtURL:url error:error];
|
|
215
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
#import "RNFileScanner.h"
|
|
2
|
+
#import "RNFileScannerCore.h"
|
|
3
|
+
#import <React/RCTBridgeModule.h>
|
|
4
|
+
|
|
5
|
+
@interface SparkScanTask : NSObject
|
|
6
|
+
@property (atomic) BOOL cancelled;
|
|
7
|
+
@end
|
|
8
|
+
@implementation SparkScanTask
|
|
9
|
+
@end
|
|
10
|
+
|
|
11
|
+
@implementation RNFileScanner {
|
|
12
|
+
BOOL _hasListeners;
|
|
13
|
+
BOOL _invalidated;
|
|
14
|
+
NSMutableDictionary<NSString *, SparkScanTask *> *_tasks;
|
|
15
|
+
}
|
|
16
|
+
RCT_EXPORT_MODULE(NativeFileScanner)
|
|
17
|
+
- (instancetype)init { if (self = [super init]) _tasks = [NSMutableDictionary new]; return self; }
|
|
18
|
+
- (NSArray<NSString *> *)supportedEvents { return @[@"onFileScanBatch", @"onFileScanProgress"]; }
|
|
19
|
+
- (void)startObserving { @synchronized(self) { _hasListeners = YES; } }
|
|
20
|
+
- (void)stopObserving { @synchronized(self) { _hasListeners = NO; } }
|
|
21
|
+
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const facebook::react::ObjCTurboModule::InitParams &)params {
|
|
22
|
+
return std::make_shared<facebook::react::NativeFileScannerSpecJSI>(params);
|
|
23
|
+
}
|
|
24
|
+
- (RNFileScannerOptions *)scanOptionsFromJSON:(NSString *)optionsJson
|
|
25
|
+
{
|
|
26
|
+
id rawValue = RNFileScannerJSONObjectFromString(optionsJson);
|
|
27
|
+
NSDictionary *rawOptions = [rawValue isKindOfClass:NSDictionary.class] ? rawValue : @{};
|
|
28
|
+
|
|
29
|
+
RNFileScannerOptions *options = [RNFileScannerOptions new];
|
|
30
|
+
options.allowedExtensions = RNFileScannerExtensionsFromArray(rawOptions[@"allowedExtensions"], @[]);
|
|
31
|
+
options.batchSize = [rawOptions[@"batchSize"] unsignedIntegerValue] ?: 64;
|
|
32
|
+
options.includeHidden = [rawOptions[@"includeHidden"] boolValue];
|
|
33
|
+
options.includeStats = [rawOptions[@"includeStats"] boolValue];
|
|
34
|
+
options.skipLookup = RNFileScannerSkipLookupFromArray(rawOptions[@"skip"]);
|
|
35
|
+
return options;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
- (BOOL)canEmitForTask:(SparkScanTask *)task {
|
|
40
|
+
@synchronized(self) { return _hasListeners && !_invalidated && !task.cancelled; }
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
- (void)scanFiles:(NSString *)identifier pathsJson:(NSString *)pathsJson optionsJson:(NSString *)optionsJson
|
|
44
|
+
resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
|
|
45
|
+
SparkScanTask *task = [SparkScanTask new];
|
|
46
|
+
@synchronized(self) {
|
|
47
|
+
if (_invalidated) { reject(@"E_CLOSED", @"Scanner has shut down", nil); return; }
|
|
48
|
+
if (_tasks[identifier]) { reject(@"E_BUSY", @"Duplicate scan ID", nil); return; }
|
|
49
|
+
_tasks[identifier] = task;
|
|
50
|
+
}
|
|
51
|
+
NSArray *paths = RNFileScannerJSONObjectFromString(pathsJson);
|
|
52
|
+
RNFileScannerOptions *options = [self scanOptionsFromJSON:optionsJson];
|
|
53
|
+
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{
|
|
54
|
+
NSDictionary *result = RNFileScannerRun(paths, options, nil,
|
|
55
|
+
^(NSArray<NSDictionary *> *items, NSUInteger rootIndex, NSUInteger completedRoots, NSUInteger totalRoots) {
|
|
56
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
57
|
+
if ([self canEmitForTask:task]) [self sendEventWithName:@"onFileScanBatch" body:@{
|
|
58
|
+
@"id": identifier, @"files": items, @"rootIndex": @(rootIndex), @"completedRoots": @(completedRoots), @"totalRoots": @(totalRoots)}];
|
|
59
|
+
});
|
|
60
|
+
},
|
|
61
|
+
^(NSUInteger rootIndex, NSUInteger completedRoots, NSUInteger totalRoots) {
|
|
62
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
63
|
+
if ([self canEmitForTask:task]) [self sendEventWithName:@"onFileScanProgress" body:@{
|
|
64
|
+
@"id": identifier, @"rootIndex": @(rootIndex), @"completedRoots": @(completedRoots), @"totalRoots": @(totalRoots)}];
|
|
65
|
+
});
|
|
66
|
+
}, ^BOOL { return task.cancelled; });
|
|
67
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
68
|
+
@synchronized(self) { [self->_tasks removeObjectForKey:identifier]; }
|
|
69
|
+
if (task.cancelled) reject(@"E_ABORTED", @"File scan cancelled", nil);
|
|
70
|
+
else resolve(RNFileScannerJSONString(result));
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
- (void)cancelScan:(NSString *)identifier resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
|
|
75
|
+
@synchronized(self) { _tasks[identifier].cancelled = YES; }
|
|
76
|
+
resolve(nil);
|
|
77
|
+
}
|
|
78
|
+
- (void)invalidate {
|
|
79
|
+
@synchronized(self) { _invalidated = YES; for (SparkScanTask *task in _tasks.allValues) task.cancelled = YES; }
|
|
80
|
+
[super invalidate];
|
|
81
|
+
}
|
|
82
|
+
@end
|
package/vendor/node_modules/@legendapp/spark-file-system/macos/file-scanner/RNFileScannerCore.h
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#import <Foundation/Foundation.h>
|
|
2
|
+
|
|
3
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
4
|
+
|
|
5
|
+
@interface RNFileScannerOptions : NSObject
|
|
6
|
+
@property (nonatomic, copy) NSSet<NSString *> *allowedExtensions;
|
|
7
|
+
@property (nonatomic, assign) NSUInteger batchSize;
|
|
8
|
+
@property (nonatomic, assign) BOOL includeHidden;
|
|
9
|
+
@property (nonatomic, assign) BOOL includeStats;
|
|
10
|
+
@property (nonatomic, copy) NSDictionary<NSNumber *, NSSet<NSString *> *> *skipLookup;
|
|
11
|
+
@end
|
|
12
|
+
|
|
13
|
+
typedef NSDictionary *_Nullable (^RNFileScannerMapFileBlock)(NSURL *fileURL,
|
|
14
|
+
NSString *rootPath,
|
|
15
|
+
NSUInteger rootIndex,
|
|
16
|
+
NSString *relativePath,
|
|
17
|
+
BOOL skipped);
|
|
18
|
+
typedef void (^RNFileScannerEmitBatchBlock)(NSArray<NSDictionary *> *items,
|
|
19
|
+
NSUInteger rootIndex,
|
|
20
|
+
NSUInteger completedRoots,
|
|
21
|
+
NSUInteger totalRoots);
|
|
22
|
+
typedef void (^RNFileScannerProgressBlock)(NSUInteger rootIndex,
|
|
23
|
+
NSUInteger completedRoots,
|
|
24
|
+
NSUInteger totalRoots);
|
|
25
|
+
|
|
26
|
+
NSString *RNFileScannerNormalizePath(NSString *path);
|
|
27
|
+
NSString *RNFileScannerRelativePath(NSString *fullPath, NSString *rootPath);
|
|
28
|
+
NSArray<NSString *> *RNFileScannerNormalizeRoots(NSArray<NSString *> *paths);
|
|
29
|
+
NSSet<NSString *> *RNFileScannerExtensionsFromArray(NSArray<NSString *> *_Nullable extensions,
|
|
30
|
+
NSArray<NSString *> *defaultExtensions);
|
|
31
|
+
NSDictionary<NSNumber *, NSSet<NSString *> *> *RNFileScannerSkipLookupFromArray(NSArray *_Nullable skipEntries);
|
|
32
|
+
NSDictionary *RNFileScannerDefaultFileItem(NSURL *fileURL,
|
|
33
|
+
NSString *rootPath,
|
|
34
|
+
NSUInteger rootIndex,
|
|
35
|
+
NSString *relativePath,
|
|
36
|
+
BOOL skipped,
|
|
37
|
+
BOOL includeStats);
|
|
38
|
+
NSDictionary *RNFileScannerRun(NSArray<NSString *> *paths,
|
|
39
|
+
RNFileScannerOptions *options,
|
|
40
|
+
RNFileScannerMapFileBlock _Nullable mapFile,
|
|
41
|
+
RNFileScannerEmitBatchBlock _Nullable emitBatch,
|
|
42
|
+
RNFileScannerProgressBlock _Nullable emitProgress,
|
|
43
|
+
BOOL (^ _Nullable isCancelled)(void));
|
|
44
|
+
NSString *RNFileScannerJSONString(id object);
|
|
45
|
+
id RNFileScannerJSONObjectFromString(NSString *json);
|
|
46
|
+
|
|
47
|
+
NS_ASSUME_NONNULL_END
|