Packaging and updates
Standalone macOS binaries, signed archives, native app updates, and SDK transfer.
Build and package a macOS app
From the generated app:
npm run build
npx --no-install spark open
npm run packagebuild embeds JavaScript and the production-selected native graph in a local ad-hoc-signed .app. package signs a staging copy with Developer ID, notarizes it with your Keychain profile, and validates the distribution ZIP. It does not publish the application.
Signing requires installed credentials, full Xcode tooling, and a notarization profile. First use can configure credentials. Keep signed archives and submission state until Apple's outcome is known. Resume/recover a pending submission through the documented packaging commands rather than starting a duplicate submission.
Intel targeting uses SPARK_MACOS_ARCH=x64 for build/package, with matching native dependencies and helper binaries. Intel native builds, runtime behavior, and signed distribution still need validation. Windows production/preview builds, signing/MSIX, Linux, and the Mac App Store workflow remain unsupported.
See packaging details.
Whole-app updates
@legendapp/spark/updates supplies native macOS whole-app update integration. It is not Expo-style JavaScript OTA updates. Runner/development/unconfigured hosts report explicit unavailability through getUpdateStatus().
import { getUpdateStatus, checkForUpdates, onUpdateEvent } from '@legendapp/spark/updates';
const subscription = onUpdateEvent(event => {
if (event.state === 'error') console.error(event.message);
else if ('version' in event) console.log(event.state, event.version);
});
const status = await getUpdateStatus();
if (status.available && status.canCheck) {
await checkForUpdates({ mode: 'interactive' });
}
subscription.remove();Retain the subscription for the app/UI lifetime; the last line illustrates teardown. Checking resolves when initiated. Progress/completion arrives as typed events: checking/notAvailable, version-bearing available/downloading/downloaded/installing, or error with message.
startUpdates starts a configured updater. configureUpdates({ automaticallyChecks?, checkIntervalSeconds? }) changes preferences; an empty object starts the configured updater without changing preferences. Feed/key configuration belongs to the native app and can require rebuilding.
Update build numbers must increase. The CLI validates a new build against the feed and earlier local artifact; equal or lower numbers reject. Test signed installation, relaunch, and rejection of older builds with your app and feed.
See update configuration and status and limitations.
Transfer a local SDK
From the framework checkout:
bun run spark sdk export /path/to/SparkSDK --runtime /path/to/SparkRunner.appThe transferable directory contains immutable package archives, checksums, an installer, and optional runtimes. --runtime can repeat. A Windows export includes the entire product directory with its DLLs. Exports refuse existing destinations and only publish output after validation.
On the recipient, preserve bytes, permissions, and symlinks, then:
cd /path/to/SparkSDK
node install.mjsUse the installed CLI command printed by the installer. Installation verifies archives before installing/registering the SDK. Network access is still needed for third-party dependencies; checksums detect corruption but are not publisher signatures.
Keep the installed directory/runtime locations stable. Relocate before installation; moving an installed SDK requires reinstalling/refreshing app references. This workflow is useful for current-source testing and does not require public npm publication.
Matching SDK runtimes
Use a Runner that matches your SDK and native dependencies. Rebuild or register a matching local Runner after SDK/native changes. Source packing, registration, and export do not publish artifacts. See SDK transfer.