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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/camera.ts +13 -0
  2. package/package.json +21 -11
  3. package/patches/camera/react-native-nitro-image.patch +320 -0
  4. package/patches/camera/react-native-nitro-modules.patch +50 -0
  5. package/patches/camera/react-native-vision-camera.patch +2662 -0
  6. package/patches/camera/upstream.json +22 -0
  7. package/patches/dependencies.json +8 -0
  8. package/patches/react-native-enriched-markdown@1.0.2.patch +8684 -0
  9. package/postinstall.cjs +1 -0
  10. package/ui-swipe.ts +1 -0
  11. package/vendor/node_modules/@legendapp/spark-ai/package.json +3 -3
  12. package/vendor/node_modules/@legendapp/spark-audio/package.json +2 -2
  13. package/vendor/node_modules/@legendapp/spark-auth-session/package.json +3 -3
  14. package/vendor/node_modules/@legendapp/spark-cli/dist/build.js +62 -11
  15. package/vendor/node_modules/@legendapp/spark-cli/dist/cli-arguments.js +2 -1
  16. package/vendor/node_modules/@legendapp/spark-cli/dist/commands.js +3 -2
  17. package/vendor/node_modules/@legendapp/spark-cli/dist/create.js +6 -3
  18. package/vendor/node_modules/@legendapp/spark-cli/dist/credentials.js +41 -26
  19. package/vendor/node_modules/@legendapp/spark-cli/dist/dependency-pins.js +132 -0
  20. package/vendor/node_modules/@legendapp/spark-cli/dist/dev-command.js +1 -1
  21. package/vendor/node_modules/@legendapp/spark-cli/dist/dev.js +1 -1
  22. package/vendor/node_modules/@legendapp/spark-cli/dist/errors.js +100 -0
  23. package/vendor/node_modules/@legendapp/spark-cli/dist/examples.js +1 -1
  24. package/vendor/node_modules/@legendapp/spark-cli/dist/expo-native.cjs +5 -1
  25. package/vendor/node_modules/@legendapp/spark-cli/dist/helpers.js +1 -1
  26. package/vendor/node_modules/@legendapp/spark-cli/dist/index.js +43 -7
  27. package/vendor/node_modules/@legendapp/spark-cli/dist/json.js +16 -0
  28. package/vendor/node_modules/@legendapp/spark-cli/dist/native-patch-preflight.js +3 -1
  29. package/vendor/node_modules/@legendapp/spark-cli/dist/process.js +7 -0
  30. package/vendor/node_modules/@legendapp/spark-cli/dist/project.js +27 -22
  31. package/vendor/node_modules/@legendapp/spark-cli/dist/release.js +2 -1
  32. package/vendor/node_modules/@legendapp/spark-cli/dist/release.json +19 -19
  33. package/vendor/node_modules/@legendapp/spark-cli/dist/universal.cjs +2 -1
  34. package/vendor/node_modules/@legendapp/spark-cli/dist/windows.js +1 -8
  35. package/vendor/node_modules/@legendapp/spark-cli/package.json +3 -3
  36. package/vendor/node_modules/@legendapp/spark-cli/templates/blank-typescript/package.json +2 -2
  37. package/vendor/node_modules/@legendapp/spark-cli/templates/universal/package.json +2 -2
  38. package/vendor/node_modules/@legendapp/spark-cli/templates/windows/package.json +2 -2
  39. package/vendor/node_modules/@legendapp/spark-clipboard/package.json +2 -2
  40. package/vendor/node_modules/@legendapp/spark-codex/package.json +3 -3
  41. package/vendor/node_modules/@legendapp/spark-commands/package.json +5 -5
  42. package/vendor/node_modules/@legendapp/spark-context-menu/package.json +2 -2
  43. package/vendor/node_modules/@legendapp/spark-desktop-app/package.json +1 -1
  44. package/vendor/node_modules/@legendapp/spark-desktop-app/src/contracts/menu.ts +6 -4
  45. package/vendor/node_modules/@legendapp/spark-desktop-config/config.cjs +2 -0
  46. package/vendor/node_modules/@legendapp/spark-desktop-config/expo.cjs +1 -0
  47. package/vendor/node_modules/@legendapp/spark-desktop-config/native-patches.cjs +106 -0
  48. package/vendor/node_modules/@legendapp/spark-desktop-config/native-patches.d.cts +1 -0
  49. package/vendor/node_modules/@legendapp/spark-desktop-config/package.json +5 -4
  50. package/vendor/node_modules/@legendapp/spark-desktop-host/package.json +2 -2
  51. package/vendor/node_modules/@legendapp/spark-desktop-links/package.json +2 -2
  52. package/vendor/node_modules/@legendapp/spark-desktop-shortcuts/package.json +2 -2
  53. package/vendor/node_modules/@legendapp/spark-desktop-windows/macos/window-manager/RNWindowManager.mm +39 -0
  54. package/vendor/node_modules/@legendapp/spark-desktop-windows/package.json +3 -3
  55. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/api.ts +3 -1
  56. package/vendor/node_modules/@legendapp/spark-desktop-windows/src/macos-adapter.ts +1 -1
  57. package/vendor/node_modules/@legendapp/spark-documents/package.json +7 -7
  58. package/vendor/node_modules/@legendapp/spark-drag-drop/package.json +2 -2
  59. package/vendor/node_modules/@legendapp/spark-file-dialog/package.json +2 -2
  60. package/vendor/node_modules/@legendapp/spark-file-system/package.json +2 -2
  61. package/vendor/node_modules/@legendapp/spark-global-shortcuts/package.json +2 -2
  62. package/vendor/node_modules/@legendapp/spark-message-dialog/package.json +2 -2
  63. package/vendor/node_modules/@legendapp/spark-native-menu/package.json +2 -2
  64. package/vendor/node_modules/@legendapp/spark-notifications/package.json +2 -2
  65. package/vendor/node_modules/@legendapp/spark-notifications/windows/SparkNotifications/SparkNotifications.h +16 -11
  66. package/vendor/node_modules/@legendapp/spark-processes/package.json +2 -2
  67. package/vendor/node_modules/@legendapp/spark-secure-storage/package.json +2 -2
  68. package/vendor/node_modules/@legendapp/spark-settings/package.json +3 -3
  69. package/vendor/node_modules/@legendapp/spark-settings-ui/package.json +4 -4
  70. package/vendor/node_modules/@legendapp/spark-sqlite/package.json +3 -3
  71. package/vendor/node_modules/@legendapp/spark-system/package.json +2 -2
  72. package/vendor/node_modules/@legendapp/spark-tray/package.json +2 -2
  73. package/vendor/node_modules/@legendapp/spark-ui/macos/appkit-split-view/RNSidebarSplitViewComponent.mm +263 -26
  74. package/vendor/node_modules/@legendapp/spark-ui/macos/swipe-actions/RNSwipeActions.h +10 -0
  75. package/vendor/node_modules/@legendapp/spark-ui/macos/swipe-actions/RNSwipeActions.mm +422 -0
  76. package/vendor/node_modules/@legendapp/spark-ui/package.json +3 -2
  77. package/vendor/node_modules/@legendapp/spark-ui/src/appkit-split-view/SidebarSplitViewNativeComponent.ts +6 -0
  78. package/vendor/node_modules/@legendapp/spark-ui/src/appkit-split-view/index.tsx +20 -11
  79. package/vendor/node_modules/@legendapp/spark-ui/src/swipe-actions/SwipeActionsNativeComponent.ts +11 -0
  80. package/vendor/node_modules/@legendapp/spark-ui/src/swipe-actions/index.tsx +41 -0
  81. package/vendor/node_modules/@legendapp/spark-updates/package.json +2 -2
  82. package/vendor/node_modules/@legendapp/spark-webview/package.json +2 -2
  83. package/vendor/node_modules/@legendapp/spark-window-options/package.json +1 -1
@@ -0,0 +1,100 @@
1
+ import { mkdirSync, writeFileSync } from "node:fs";
2
+ import path from "node:path";
3
+ // Command failures retain diagnostics without making callers parse their message.
4
+ export class CommandError extends Error {
5
+ argv;
6
+ code;
7
+ output;
8
+ log;
9
+ constructor(argv, code, output, log) {
10
+ super(`${argv.join(" ")} exited ${code}${log ? `. See ${log}` : ""}${output ? `\n${output.slice(-1800)}` : ""}`);
11
+ this.argv = argv;
12
+ this.code = code;
13
+ this.output = output;
14
+ this.log = log;
15
+ this.name = "CommandError";
16
+ }
17
+ }
18
+ export function isNetworkError(error) {
19
+ const message = error instanceof CommandError ? error.output : error instanceof Error ? error.message : String(error);
20
+ return /NSURLErrorDomain Code=-(?:1001|1005|1009)|internet connection appears to be offline|\b(?:ENOTFOUND|EAI_AGAIN|ETIMEDOUT|ECONNRESET|ENETUNREACH)\b|could not resolve host|failed to connect|network is unreachable/i.test(message);
21
+ }
22
+ export function errorPresentation(error) {
23
+ const message = error instanceof Error ? error.message : String(error);
24
+ const command = error instanceof CommandError ? error.argv.join(" ") : "";
25
+ const details = error instanceof CommandError ? error.output : message;
26
+ let summary = message.split("\n")[0] || "An unexpected error occurred.";
27
+ let next;
28
+ if (isNetworkError(error)) {
29
+ summary = command.includes("notarytool") ? "Could not reach Apple’s notarization service." : "Could not reach the remote service.";
30
+ next = "Check your internet connection or VPN, then retry the same command.";
31
+ }
32
+ else if ((/\b(?:E401|ENEEDAUTH)\b|401 Unauthorized/i.test(details) && /npm/.test(command || message)) || command === "npm whoami") {
33
+ summary = "npm authentication failed.";
34
+ next = "Run npm login, then retry the same release command.";
35
+ }
36
+ else if (command === "gh auth status" || /gh auth login/.test(details)) {
37
+ summary = "GitHub authentication failed.";
38
+ next = "Run gh auth login, then retry the same command.";
39
+ }
40
+ else if (/No Keychain password item found for profile:/.test(details)) {
41
+ summary = "The selected notarization Keychain profile was not found.";
42
+ next = "Run spark credentials to select an existing profile or explicitly create one.";
43
+ }
44
+ else if (/notarytool/.test(command) && /\b401\b|invalid credentials|authentication/i.test(details)) {
45
+ summary = "Apple rejected the notarization credentials.";
46
+ next = "Run spark credentials to select or explicitly create a valid Keychain profile.";
47
+ }
48
+ else if (error instanceof CommandError) {
49
+ const checks = { "bun run test": "Tests failed.", "bun run typecheck": "Type checking failed.", "bun install --frozen-lockfile": "Dependency installation failed." };
50
+ summary = checks[command] ?? `${command} failed (exit ${error.code}).`;
51
+ next = "Review the diagnostic log or output above, fix the problem, then retry the same command.";
52
+ }
53
+ return { summary, next };
54
+ }
55
+ function diagnostics(error, seen = new Set()) {
56
+ let result = String(error);
57
+ if (error instanceof Error && !seen.has(error)) {
58
+ seen.add(error);
59
+ result = error.stack ?? error.message;
60
+ if (error instanceof CommandError && error.output)
61
+ result += `\n\nCommand output:\n${error.output}`;
62
+ if (error.cause)
63
+ result += `\n\nCaused by:\n${diagnostics(error.cause, seen)}`;
64
+ }
65
+ return result;
66
+ }
67
+ export function reportError(root, error, label = "Release") {
68
+ const { summary, next } = errorPresentation(error);
69
+ console.error(`\n${label} stopped: ${summary}`);
70
+ if (next)
71
+ console.error(`Next: ${next}`);
72
+ let log;
73
+ try {
74
+ const folder = path.join(root, ".spark/logs");
75
+ mkdirSync(folder, { recursive: true });
76
+ const destination = path.join(folder, `${Date.now()}-${crypto.randomUUID()}-error.log`);
77
+ writeFileSync(destination, diagnostics(error));
78
+ log = destination;
79
+ }
80
+ catch {
81
+ // Reporting must still work when the original failure was a full disk.
82
+ if (error instanceof CommandError)
83
+ log = error.log;
84
+ }
85
+ if (log)
86
+ console.error(`Details: ${log}`);
87
+ else
88
+ console.error("Could not save diagnostics. Set SPARK_DEBUG=1 to print them.");
89
+ if (process.env.SPARK_DEBUG === "1")
90
+ console.error(diagnostics(error));
91
+ }
92
+ export async function releaseCommand(root, action) {
93
+ try {
94
+ await action();
95
+ }
96
+ catch (error) {
97
+ reportError(root, error);
98
+ process.exitCode = 1;
99
+ }
100
+ }
@@ -1,7 +1,7 @@
1
1
  import { packageManager, managerCommand } from "./package-manager.js";
2
2
  import { cpSync, writeFileSync } from "node:fs";
3
3
  import path from "node:path";
4
- import { readJson, writeJson } from "./project.js";
4
+ import { readJson, writeJson } from "./json.js";
5
5
  import { run } from "./commands.js";
6
6
  export const examples = ["document-editor", "notes-lite", "music-lite", "diff-lite"];
7
7
  export async function configureExample(root, example) {
@@ -1,5 +1,9 @@
1
1
  exports.withSparkNative = (original, root) => {
2
- if (!['macos', 'windows'].includes(process.env.SPARK_PLATFORM)) return original;
2
+ if (!['macos', 'windows'].includes(process.env.SPARK_PLATFORM)) {
3
+ const patched = require('@legendapp/spark-desktop-config/native-patches.cjs').prepareNativePatches(root);
4
+ if (!Object.keys(patched).length) return original;
5
+ return { ...original, dependencies: { ...patched, ...original.dependencies } };
6
+ }
3
7
  const generated = require('./universal.cjs').nativeConfig(root);
4
8
  const dependencies = { ...original.dependencies };
5
9
  for (const [name, config] of Object.entries(generated.dependencies ?? {})) {
@@ -2,8 +2,8 @@ import { copyFileSync, lstatSync, mkdirSync, realpathSync, chmodSync, readdirSyn
2
2
  import path from "node:path";
3
3
  import { createRequire } from "node:module";
4
4
  import { architecture } from "./platform.js";
5
- const { validateHelpers } = createRequire(import.meta.url)("@legendapp/spark-desktop-config/helpers.cjs");
6
5
  export function resolveHelpers(root, helpers = {}, platform = "macos", arch = architecture(platform)) {
6
+ const { validateHelpers } = createRequire(import.meta.url)("@legendapp/spark-desktop-config/helpers.cjs");
7
7
  validateHelpers(helpers);
8
8
  const base = realpathSync(root);
9
9
  function inspect(file) {
@@ -1,4 +1,4 @@
1
- import { spawnProcess } from "./process.js";
1
+ import { nodeCommand, spawnProcess } from "./process.js";
2
2
  import { examples } from "./examples.js";
3
3
  import { prepareGoProfile } from "./go-profile.js";
4
4
  import { exportSDK, importSDK } from "./sdk-transfer.js";
@@ -7,7 +7,7 @@ import path from "node:path";
7
7
  import { cliArguments } from "./cli-arguments.js";
8
8
  import { existsSync, readFileSync, writeFileSync } from "node:fs";
9
9
  import { prepareConfig, readConfig, isExpoProject } from "@legendapp/spark-desktop-config/config.cjs";
10
- import { nodeCommand, prepareWindows } from "./windows.js";
10
+ import { prepareWindows } from "./windows.js";
11
11
  import { create, refreshLocalPackages } from "./create.js";
12
12
  import { addDesktop } from "./add-desktop.js";
13
13
  import { buildMode } from "./build-mode.js";
@@ -20,6 +20,8 @@ import { doctor, run } from "./commands.js";
20
20
  import { findFramework, findProject, sparkHome, creationManifest, packageManifest, registerRuntime } from "./local.js";
21
21
  import { readJson, stateFile, VERSION, writeJson } from "./project.js";
22
22
  import { devCommand } from "./dev-command.js";
23
+ import { printDependencyPins, upgradeDependencyPins } from "./dependency-pins.js";
24
+ import { reportError } from "./errors.js";
23
25
  async function main() {
24
26
  const argv = process.argv.slice(2);
25
27
  if (argv[0] === "dev")
@@ -78,6 +80,8 @@ async function main() {
78
80
  spark build Build a standalone desktop app
79
81
  spark prebuild Generate a Windows/mobile native project
80
82
  spark package Sign and notarize a distribution archive
83
+ spark doctor Check app dependency pins and native tooling (exits 1 on issues)
84
+ spark upgrade Align app dependency pins with Spark (--dry-run previews changes)
81
85
 
82
86
  Inside an app: npm run dev, npm run build, npm run package (or pnpm, yarn, bun)
83
87
 
@@ -99,8 +103,6 @@ Overrides: --project <directory>, --port <number>, dev --runner-binary <runtime
99
103
  case "create": {
100
104
  if (!positionals[1])
101
105
  throw new Error("Usage: spark create MyApp");
102
- if (!(values.universal || values.example) && !["macos", "windows"].includes(platform))
103
- throw new Error("Use create --universal for mobile/web targets");
104
106
  await create(path.resolve(positionals[1]), creationManifest(values.packages), platform, !!values.universal || !!values.example, values.example, values["package-manager"]);
105
107
  break;
106
108
  }
@@ -173,10 +175,44 @@ Overrides: --project <directory>, --port <number>, dev --runner-binary <runtime
173
175
  throw new Error("For macOS, spark build --dev generates and builds the native project.");
174
176
  await prepareWindows(project(), "dev");
175
177
  break;
176
- case "doctor":
177
- await doctor(start);
178
+ case "doctor": {
179
+ let root;
180
+ let pinIssues = false;
181
+ try {
182
+ root = project();
183
+ }
184
+ catch {
185
+ console.log("No Spark app found; skipping dependency pin check.");
186
+ }
187
+ if (root) {
188
+ try {
189
+ const issues = upgradeDependencyPins(root, true);
190
+ printDependencyPins(issues);
191
+ if (issues.length) {
192
+ process.exitCode = 1;
193
+ pinIssues = true;
194
+ }
195
+ }
196
+ catch (error) {
197
+ console.error(`Dependency pin check failed: ${error instanceof Error ? error.message : String(error)}`);
198
+ process.exitCode = 1;
199
+ pinIssues = true;
200
+ }
201
+ }
202
+ await doctor(root ?? start);
178
203
  console.log("Native toolchain available.");
204
+ if (pinIssues)
205
+ console.log("Dependency pin issues found; run spark upgrade.");
206
+ break;
207
+ }
208
+ case "upgrade": {
209
+ const issues = upgradeDependencyPins(project(), !!values["dry-run"]);
210
+ console.log(values["dry-run"] ? "Dependency pin changes (dry run):" : "Dependency pin changes:");
211
+ printDependencyPins(issues);
212
+ if (issues.length)
213
+ console.log(values["dry-run"] ? "No files changed. Run spark upgrade, then run your package manager's install command (npm/pnpm/yarn/bun install)." : "Updated package.json. Run your package manager's install command (npm/pnpm/yarn/bun install).");
179
214
  break;
215
+ }
180
216
  case "updates": {
181
217
  if (positionals[1] !== "init" || !positionals[2])
182
218
  throw new Error("Usage: spark updates init https://example.com/updates/appcast.xml");
@@ -230,6 +266,6 @@ try {
230
266
  await main();
231
267
  }
232
268
  catch (error) {
233
- console.error(error instanceof Error ? error.message : String(error));
269
+ reportError(process.cwd(), error, "Spark");
234
270
  process.exitCode = 1;
235
271
  }
@@ -0,0 +1,16 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, renameSync } from "node:fs";
2
+ import path from "node:path";
3
+ export function readJson(file) {
4
+ return JSON.parse(readFileSync(file, "utf8"));
5
+ }
6
+ export function writeJson(file, value) {
7
+ const content = JSON.stringify(value, null, 2) + "\n";
8
+ // Metro watches JSON too: rewriting unchanged session state on every runtime
9
+ // check causes empty Fast Refresh updates even when app code has not changed.
10
+ if (existsSync(file) && readFileSync(file, "utf8") === content)
11
+ return;
12
+ mkdirSync(path.dirname(file), { recursive: true });
13
+ const temporary = `${file}.${process.pid}.tmp`;
14
+ writeFileSync(temporary, content);
15
+ renameSync(temporary, file);
16
+ }
@@ -1,6 +1,7 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { existsSync, readFileSync } from "node:fs";
3
3
  import path from "node:path";
4
+ import { prepareNativePatches } from "@legendapp/spark-desktop-config/native-patches.cjs";
4
5
  import { findFramework } from "./local.js";
5
6
  import { installedPackages, readJson, VERSION } from "./project.js";
6
7
  const required = ["@react-native-runtimes/core", "react-native-nitro-modules", "@op-engineering/op-sqlite", "react-native-webview"];
@@ -67,6 +68,7 @@ export function validateWorkspaceNativePatches(root, packages) {
67
68
  return errors;
68
69
  }
69
70
  export function assertNativePatchPreflight(root) {
71
+ prepareNativePatches(root);
70
72
  const packages = installedPackages(root);
71
73
  const framework = findFramework(root);
72
74
  let errors;
@@ -80,5 +82,5 @@ export function assertNativePatchPreflight(root) {
80
82
  errors = validatePackedNativePatches(packages, requirements);
81
83
  }
82
84
  if (errors.length)
83
- throw new Error(`Native preparation stopped before changing project files:\n${errors.map(error => `- ${error}`).join("\n")}`);
85
+ throw new Error(`Native patch preparation stopped before changing project files:\n${errors.map(error => `- ${error}`).join("\n")}`);
84
86
  }
@@ -1,3 +1,4 @@
1
+ import { readJson } from "./json.js";
1
2
  import path from "node:path";
2
3
  import { spawn as nodeSpawn } from "node:child_process";
3
4
  import { createRequire } from "node:module";
@@ -55,3 +56,9 @@ export function spawnProcess(argv, options = {}) {
55
56
  return result;
56
57
  }
57
58
  export { which } from "./executable.js";
59
+ // Windows .cmd shims are shell programs; run the package's actual JS entry.
60
+ export function nodeCommand(root, name, bin, args = []) {
61
+ const req = createRequire(path.join(root, "package.json"));
62
+ const file = req.resolve(`${name}/package.json`), pkg = readJson(file);
63
+ return ["node", path.resolve(path.dirname(file), typeof pkg.bin === "string" ? pkg.bin : pkg.bin[bin]), ...args];
64
+ }
@@ -1,4 +1,5 @@
1
1
  import { nativeWindowOptions } from "@legendapp/spark-window-options";
2
+ import { prepareNativePatches } from "@legendapp/spark-desktop-config/native-patches.cjs";
2
3
  import { macOSReleaseSettings } from "./macos-release.js";
3
4
  import { projectPlatform, architecture } from "./platform.js";
4
5
  import { resolveHelpers } from "./helpers.js";
@@ -6,23 +7,11 @@ import { readConfig as readAppConfig, prepareConfig, writeUpdates, statePath, is
6
7
  export { readAppConfig, prepareConfig, writeUpdates, statePath, isUniversal };
7
8
  import { createHash } from "node:crypto";
8
9
  import { createRequire } from "node:module";
9
- import { existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, writeFileSync, renameSync, } from "node:fs";
10
+ import { existsSync, readFileSync, readdirSync, realpathSync, } from "node:fs";
10
11
  import path from "node:path";
11
- export const VERSION = "0.0.1-next.3";
12
- export function readJson(file) {
13
- return JSON.parse(readFileSync(file, "utf8"));
14
- }
15
- export function writeJson(file, value) {
16
- const content = JSON.stringify(value, null, 2) + "\n";
17
- // Metro watches JSON too: rewriting unchanged session state on every runtime
18
- // check causes empty Fast Refresh updates even when app code has not changed.
19
- if (existsSync(file) && readFileSync(file, "utf8") === content)
20
- return;
21
- mkdirSync(path.dirname(file), { recursive: true });
22
- const temporary = `${file}.${process.pid}.tmp`;
23
- writeFileSync(temporary, content);
24
- renameSync(temporary, file);
25
- }
12
+ import { readJson, writeJson } from "./json.js";
13
+ export { readJson, writeJson };
14
+ export const VERSION = "0.0.1-next.4";
26
15
  export function digest(value) {
27
16
  return createHash("sha256").update(value).digest("hex");
28
17
  }
@@ -134,17 +123,31 @@ export function hashFiles(root, entries, windows = false) {
134
123
  visit(entry);
135
124
  return hash.digest("hex");
136
125
  }
137
- export function nativePackages(root) {
126
+ export function nativePackages(root, { applyExcludes = true } = {}) {
127
+ prepareNativePatches(root);
138
128
  const installed = installedPackages(root);
139
129
  if (projectPlatform(root) === "windows")
140
130
  return windowsNativePackages(root, installed);
131
+ const excluded = ["app.json", "desktop.config.json"].some(file => existsSync(path.join(root, file)))
132
+ ? readAppConfig(root).expo?.autolinking?.exclude ?? [] : [];
133
+ if (!applyExcludes) {
134
+ const appRequire = createRequire(path.join(root, "package.json"));
135
+ for (const name of excluded) {
136
+ if (installed.some(pkg => pkg.name === name))
137
+ continue;
138
+ const file = appRequire.resolve.paths(name)?.map(base => path.join(base, name, "package.json")).find(file => existsSync(file));
139
+ if (!file)
140
+ continue;
141
+ const json = readJson(file);
142
+ installed.push({ name: json.name, root: realpathSync(path.dirname(file)), json });
143
+ }
144
+ installed.sort((a, b) => a.name.localeCompare(b.name));
145
+ }
141
146
  // Host/CNG code can change the native ABI without adding a TurboModule.
142
147
  // Fold it into the mandatory app module's compatibility signature for Go.
143
148
  const adapters = installed.filter(pkg => ["@legendapp/spark-desktop-host", "@legendapp/spark-desktop-config"].includes(pkg.name))
144
149
  .map(pkg => hashFiles(pkg.root, ["package.json", "AppDelegate.mm", ...readdirSync(pkg.root).filter(name => name.endsWith(".cjs"))])).join(":");
145
- const excluded = ["app.json", "desktop.config.json"].some(file => existsSync(path.join(root, file)))
146
- ? readAppConfig(root).expo?.autolinking?.exclude ?? [] : [];
147
- return installed.filter(pkg => !excluded.includes(pkg.name))
150
+ return installed.filter(pkg => !applyExcludes || !excluded.includes(pkg.name))
148
151
  .filter((pkg) => pkg.json.codegenConfig ||
149
152
  pkg.json.spark?.nativeModules ||
150
153
  readdirSync(pkg.root).some((name) => name.endsWith(".podspec") || name === "expo-module.config.json"))
@@ -181,12 +184,12 @@ export function dependencyStamp(root) {
181
184
  "react-native.config.js",
182
185
  ]);
183
186
  }
184
- export function selection(packages, used, include = []) {
187
+ export function selection(packages, used, include = [], excluded = []) {
185
188
  const selected = new Set([
186
189
  ...packages.filter((p) => !p.sdk).map((p) => p.name),
187
190
  ...used,
188
191
  ...include,
189
- ]);
192
+ ].filter(name => !excluded.includes(name)));
190
193
  const reasons = {};
191
194
  for (const pkg of packages)
192
195
  if (selected.has(pkg.name))
@@ -209,6 +212,8 @@ export function selection(packages, used, include = []) {
209
212
  for (const name of nativeDeps) {
210
213
  if (!packages.some((p) => p.name === name))
211
214
  throw new Error(`${pkg.name} requires missing native package ${name}`);
215
+ if (excluded.includes(name))
216
+ continue;
212
217
  if (!selected.has(name)) {
213
218
  selected.add(name);
214
219
  reasons[name] = `native dependency of ${pkg.name}`;
@@ -1,8 +1,9 @@
1
1
  import { createHash, randomUUID } from "node:crypto";
2
2
  import { closeSync, existsSync, mkdirSync, openSync, readFileSync, renameSync, rmSync, writeSync } from "node:fs";
3
3
  import path from "node:path";
4
- import { readJson, VERSION } from "./project.js";
4
+ import { readJson } from "./json.js";
5
5
  import { localArchive } from "./package-manager.js";
6
+ const VERSION = readJson(path.resolve(import.meta.dirname, "../package.json")).version;
6
7
  export const releaseBase = (version) => `https://github.com/LegendApp/legend-spark/releases/download/v${version}`;
7
8
  export function validateRelease(value) {
8
9
  if (value?.schema !== 1 || value.version !== VERSION || !/^[a-f0-9]{40}$/.test(value.revision) || !value.packages || !value.runners)
@@ -1,42 +1,42 @@
1
1
  {
2
2
  "schema": 1,
3
- "version": "0.0.1-next.3",
4
- "revision": "5aa7d342e3901b13b3947e61f0115e110857e940",
3
+ "version": "0.0.1-next.4",
4
+ "revision": "23bfaba5d57e9eda98422f1187fd65037ac11d01",
5
5
  "packages": {
6
6
  "@react-native-runtimes/core": {
7
- "url": "https://github.com/LegendApp/legend-spark/releases/download/v0.0.1-next.3/runtimes-a216ebc90023.tgz",
7
+ "url": "https://github.com/LegendApp/legend-spark/releases/download/v0.0.1-next.4/runtimes-985164a87eb0.tgz",
8
8
  "size": 53886,
9
- "sha256": "acf7330c6e4ba50c5d81dc8459ef4195cd5220245d49bbedf44dd7d241d6e804"
9
+ "sha256": "138644fcfaf60b172c9f206c66c6d160515dd8803c70264687d0f503294c9c0b"
10
10
  },
11
11
  "react-native-nitro-modules": {
12
- "url": "https://github.com/LegendApp/legend-spark/releases/download/v0.0.1-next.3/react-native-nitro-modules-0.35.7-e26b0b759292.tgz",
13
- "size": 138698,
14
- "sha256": "e26b0b7592929d779a488623dbe936a37d3cd169228af8207d05faf65e452c17"
12
+ "url": "https://github.com/LegendApp/legend-spark/releases/download/v0.0.1-next.4/react-native-nitro-modules-0.37.0-56a36548e4e3.tgz",
13
+ "size": 145386,
14
+ "sha256": "56a36548e4e3236d3805dc75774a1c293034318d2825580c0b86d4079cce80ae"
15
15
  },
16
16
  "@op-engineering/op-sqlite": {
17
- "url": "https://github.com/LegendApp/legend-spark/releases/download/v0.0.1-next.3/op-engineering-op-sqlite-18.2.1-d347b5859c8e.tgz",
18
- "size": 112508638,
19
- "sha256": "d347b5859c8ede8f13a1195b62d84129d3c519ddb983c5af241953841fb2ce4a"
17
+ "url": "https://github.com/LegendApp/legend-spark/releases/download/v0.0.1-next.4/op-engineering-op-sqlite-18.2.1-809aacdae6a3.tgz",
18
+ "size": 112508531,
19
+ "sha256": "809aacdae6a375f1524022533800253ee89beb931cdb5ab02781acecfa53aeac"
20
20
  },
21
21
  "react-native-webview": {
22
- "url": "https://github.com/LegendApp/legend-spark/releases/download/v0.0.1-next.3/react-native-webview-16.0.0-3b95dfeffdd6.tgz",
22
+ "url": "https://github.com/LegendApp/legend-spark/releases/download/v0.0.1-next.4/react-native-webview-16.0.0-bc2c08eb8de7.tgz",
23
23
  "size": 116287,
24
- "sha256": "3b95dfeffdd6d5a12179b367c846dfdca4fa95421ba1fa9c5e20617bc89b82fc"
24
+ "sha256": "bc2c08eb8de7b979d90a641ce63b3241c034f836c76c77b794716473b3b43216"
25
25
  },
26
26
  "expo-audio": {
27
- "url": "https://github.com/LegendApp/legend-spark/releases/download/v0.0.1-next.3/expo-audio-1.1.1-226962e4b7a3.tgz",
28
- "size": 381077,
29
- "sha256": "226962e4b7a36596e859524104037f35d33b25ff2f675c88b54f222677ca916b"
27
+ "url": "https://github.com/LegendApp/legend-spark/releases/download/v0.0.1-next.4/expo-audio-1.1.1-c4ae56565c36.tgz",
28
+ "size": 381084,
29
+ "sha256": "c4ae56565c365086e134ab9edd15ce981a2149bda07d9e9553e151def5fe7fec"
30
30
  }
31
31
  },
32
32
  "runners": {
33
33
  "macos-arm64": {
34
- "url": "https://github.com/LegendApp/legend-spark/releases/download/v0.0.1-next.3/spark-runner-macos-arm64.zip",
35
- "size": 13043465,
36
- "sha256": "a33a4adf683579e18b05b16d680ece0c664403bdcd2f846fdb955b5d2fe3f7d8",
34
+ "url": "https://github.com/LegendApp/legend-spark/releases/download/v0.0.1-next.4/spark-runner-macos-arm64.zip",
35
+ "size": 17395353,
36
+ "sha256": "6334c66020c8ddd0ff3f6c154b8fb97e01ceb952a26254765b816bd6055dabb1",
37
37
  "app": "SparkRunner.app",
38
38
  "teamId": "56DD97VT22",
39
- "fingerprint": "6925832e8790b3253cb7509aacc5978ee90da2e15b884492624b1b43945291f5"
39
+ "fingerprint": "64932dc5718a49253e5f76a9ae26c3553059893b1e0c6dcff7b05f86c2c787e2"
40
40
  }
41
41
  }
42
42
  }
@@ -2,13 +2,14 @@ const fs = require("node:fs");
2
2
  const { readConfig, statePath, supportedPlatforms } = require("@legendapp/spark-desktop-config/config.cjs");
3
3
 
4
4
  exports.nativeConfig = root => {
5
+ const patched = require("@legendapp/spark-desktop-config/native-patches.cjs").prepareNativePatches(root);
5
6
  const { expo } = readConfig(root);
6
7
  const platform = expo.platforms[0];
7
8
  const platforms = process.platform === "win32" ? {} : { windows: { npmPackageName: "react-native-windows" } };
8
9
  // Expo autolinking and React Native codegen have separate exclusion inputs.
9
10
  // An excluded desktop provider must not enter an iOS module registry either.
10
11
  const excluded = (expo.autolinking?.exclude ?? []).map(name => [name, { platforms: { ios: null, macos: null, android: null, windows: null } }]);
11
- if (!["macos", "windows"].includes(platform)) return { platforms, dependencies: Object.fromEntries(excluded) };
12
+ if (!["macos", "windows"].includes(platform)) return { platforms, dependencies: { ...patched, ...Object.fromEntries(excluded) } };
12
13
  const file = statePath(root, "native-selection.json");
13
14
  const selection = fs.existsSync(file) ? JSON.parse(fs.readFileSync(file, "utf8")) : { included: [], excluded: [] };
14
15
  return { platforms, dependencies: Object.fromEntries([
@@ -1,20 +1,13 @@
1
- import { which } from "./process.js";
1
+ import { which, nodeCommand } from "./process.js";
2
2
  import { prepareWindowsGeometry } from "./windows-geometry.js";
3
3
  import { copyHelpers } from "./helpers.js";
4
4
  import { registerWindowsAssociations } from "./windows-associations.js";
5
5
  import { checkExpoDesktopNode } from "./expo-node.js";
6
6
  import { cpSync, existsSync, mkdirSync, mkdtempSync, readdirSync, renameSync, rmSync, readFileSync, writeFileSync } from "node:fs";
7
7
  import path from "node:path";
8
- import { createRequire } from "node:module";
9
8
  import { run } from "./commands.js";
10
9
  import { prepareConfig, nativePackages, runtimeFor, stateFile, readJson, writeJson, validateBuildModules, goConfigurationIssues, readAppConfig } from "./project.js";
11
10
  import { assertNativePatchPreflight } from "./native-patch-preflight.js";
12
- // Windows .cmd shims are shell programs; run the package's actual JS entry.
13
- export function nodeCommand(root, name, bin, args = []) {
14
- const req = createRequire(path.join(root, "package.json"));
15
- const file = req.resolve(`${name}/package.json`), pkg = readJson(file);
16
- return ["node", path.resolve(path.dirname(file), typeof pkg.bin === "string" ? pkg.bin : pkg.bin[bin]), ...args];
17
- }
18
11
  export async function preserveWindowsBuildOutputs(root, prepare) {
19
12
  const windows = path.join(root, "windows");
20
13
  const outputs = [];
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@legendapp/spark-cli",
3
- "version": "0.0.1-next.3",
3
+ "version": "0.0.1-next.4",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "files": [
@@ -11,13 +11,13 @@
11
11
  "node": ">=24.19.0"
12
12
  },
13
13
  "dependencies": {
14
- "@legendapp/spark-desktop-config": "0.0.1-next.3",
14
+ "@legendapp/spark-desktop-config": "0.0.1-next.4",
15
15
  "ws": "8.21.0",
16
16
  "expo-desktop": "1.0.0",
17
17
  "npm": "11.21.0",
18
18
  "typescript": "5.9.3",
19
19
  "cross-spawn": "7.0.6",
20
- "@legendapp/spark-window-options": "0.0.1-next.3"
20
+ "@legendapp/spark-window-options": "0.0.1-next.4"
21
21
  },
22
22
  "private": true
23
23
  }
@@ -13,7 +13,7 @@
13
13
  "postinstall": "node node_modules/@legendapp/spark/init-template.cjs"
14
14
  },
15
15
  "dependencies": {
16
- "@legendapp/spark": "0.0.1-next.3",
16
+ "@legendapp/spark": "0.0.1-next.4",
17
17
  "expo": "54.0.37",
18
18
  "expo-desktop": "1.0.0",
19
19
  "expo-desktop-prebuild-config": "1.1.0",
@@ -30,7 +30,7 @@
30
30
  },
31
31
  "overrides": {
32
32
  "@expo/cli": "54.0.27",
33
- "react-native-nitro-modules": "0.35.7",
33
+ "react-native-nitro-modules": "0.37.0",
34
34
  "react-native-webview": "16.0.0",
35
35
  "@op-engineering/op-sqlite": "18.2.1",
36
36
  "expo-asset": "12.0.13",
@@ -41,11 +41,11 @@
41
41
  "react-native-web": "0.21.0",
42
42
  "uniwind": "1.6.3",
43
43
  "tailwindcss": "4.2.4",
44
- "@legendapp/spark": "0.0.1-next.3"
44
+ "@legendapp/spark": "0.0.1-next.4"
45
45
  },
46
46
  "overrides": {
47
47
  "@expo/cli": "54.0.27",
48
- "react-native-nitro-modules": "0.35.7",
48
+ "react-native-nitro-modules": "0.37.0",
49
49
  "react-native-webview": "16.0.0",
50
50
  "@op-engineering/op-sqlite": "18.2.1",
51
51
  "expo-asset": "12.0.13",
@@ -27,11 +27,11 @@
27
27
  "react-native-windows": "0.81.35",
28
28
  "expo-desktop-modules-core": "54.0.14",
29
29
  "expo-desktop-stubs": "54.0.14",
30
- "@legendapp/spark": "0.0.1-next.3"
30
+ "@legendapp/spark": "0.0.1-next.4"
31
31
  },
32
32
  "overrides": {
33
33
  "@expo/cli": "54.0.27",
34
- "react-native-nitro-modules": "0.35.7",
34
+ "react-native-nitro-modules": "0.37.0",
35
35
  "react-native-webview": "16.0.0",
36
36
  "@op-engineering/op-sqlite": "18.2.1",
37
37
  "expo-asset": "12.0.13",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@legendapp/spark-clipboard",
3
- "version": "0.0.1-next.3",
3
+ "version": "0.0.1-next.4",
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.3"
44
+ "@legendapp/spark-desktop-app": "0.0.1-next.4"
45
45
  },
46
46
  "peerDependenciesMeta": {
47
47
  "expo-clipboard": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@legendapp/spark-codex",
3
- "version": "0.0.1-next.3",
3
+ "version": "0.0.1-next.4",
4
4
  "private": true,
5
5
  "main": "src/index.ts",
6
6
  "react-native": "src/index.ts",
@@ -10,8 +10,8 @@
10
10
  },
11
11
  "license": "MIT",
12
12
  "dependencies": {
13
- "@legendapp/spark-desktop-app": "0.0.1-next.3",
14
- "react-native-nitro-modules": "0.35.7"
13
+ "@legendapp/spark-desktop-app": "0.0.1-next.4",
14
+ "react-native-nitro-modules": "0.37.0"
15
15
  },
16
16
  "files": [
17
17
  "src",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@legendapp/spark-commands",
3
- "version": "0.0.1-next.3",
3
+ "version": "0.0.1-next.4",
4
4
  "private": true,
5
5
  "license": "MIT",
6
6
  "main": "src/index.tsx",
@@ -8,11 +8,11 @@
8
8
  "src"
9
9
  ],
10
10
  "dependencies": {
11
- "@legendapp/spark-ui": "0.0.1-next.3",
12
- "@legendapp/spark-desktop-shortcuts": "0.0.1-next.3",
13
- "@legendapp/spark-settings": "0.0.1-next.3",
11
+ "@legendapp/spark-ui": "0.0.1-next.4",
12
+ "@legendapp/spark-desktop-shortcuts": "0.0.1-next.4",
13
+ "@legendapp/spark-settings": "0.0.1-next.4",
14
14
  "@legendapp/state": "^3.0.0-beta.42",
15
- "@legendapp/spark-desktop-app": "0.0.1-next.3"
15
+ "@legendapp/spark-desktop-app": "0.0.1-next.4"
16
16
  },
17
17
  "peerDependencies": {
18
18
  "react": "19.1.4",