craft-native 0.0.276 → 0.0.290

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/dist/cli.js CHANGED
@@ -130,18 +130,21 @@ import { spawn } from "child_process";
130
130
  import { createHash } from "crypto";
131
131
  import {
132
132
  chmodSync,
133
+ closeSync,
133
134
  copyFileSync,
134
135
  cpSync,
135
136
  existsSync as existsSync4,
136
137
  lstatSync,
137
138
  mkdirSync,
139
+ openSync,
138
140
  readFileSync as readFileSync2,
141
+ readSync,
139
142
  readdirSync as readdirSync2,
140
143
  rmSync as rmSync2,
141
144
  writeFileSync as writeFileSync3
142
145
  } from "fs";
143
146
  import { homedir } from "os";
144
- import { basename, join as join2 } from "path";
147
+ import { basename, join as join2, resolve } from "path";
145
148
  import { deflateRawSync } from "zlib";
146
149
  function crc32(data) {
147
150
  let c = 4294967295;
@@ -243,8 +246,10 @@ async function packageMacOS(config, outDir) {
243
246
  const { name, version, bundleId = `com.myapp.${name.toLowerCase()}` } = config;
244
247
  const opts = config.macos || {};
245
248
  const hardenedRuntime = !opts.appStore;
249
+ if (opts.appStore && (!opts.entitlements || !opts.provisioningProfile))
250
+ return [{ success: false, platform: "macos", format: "app", error: "App Store packaging requires macos.entitlements and macos.provisioningProfile" }];
246
251
  const appBundlePath = join2(outDir, `${name}.app`);
247
- const appBundle = createMacOSAppBundle({
252
+ const appBundle = opts.appBundlePath ? copyMacOSAppBundle(opts.appBundlePath, appBundlePath) : createMacOSAppBundle({
248
253
  name,
249
254
  version,
250
255
  bundleId,
@@ -268,25 +273,42 @@ async function packageMacOS(config, outDir) {
268
273
  });
269
274
  return results;
270
275
  }
271
- if (opts.signIdentity) {
272
- const signed = await runTool("codesign", codesignArguments({
273
- path: appBundlePath,
274
- identity: opts.signIdentity,
275
- entitlements: opts.entitlements,
276
- hardenedRuntime
277
- }));
278
- if (!signed.success) {
279
- results.push({ success: false, platform: "macos", format: "app", error: signed.error });
280
- return results;
276
+ try {
277
+ const contents = join2(appBundlePath, "Contents");
278
+ if (opts.provisioningProfile) {
279
+ const profile = join2(contents, "embedded.provisionprofile");
280
+ copyFileSync(opts.provisioningProfile, profile);
281
+ chmodSync(profile, 420);
282
+ }
283
+ if (opts.privacyManifest)
284
+ copyFileSync(opts.privacyManifest, join2(contents, "Resources", "PrivacyInfo.xcprivacy"));
285
+ if (opts.appBundlePath) {
286
+ const metadata = {
287
+ CFBundleVersion: opts.buildNumber,
288
+ LSApplicationCategoryType: opts.category,
289
+ LSMinimumSystemVersion: opts.minimumSystemVersion,
290
+ LSUIElement: opts.menuBarOnly
291
+ };
292
+ if (config.iconPath) {
293
+ const icon = basename(config.iconPath);
294
+ copyFileSync(config.iconPath, join2(contents, "Resources", icon));
295
+ metadata.CFBundleIconFile = icon;
296
+ }
297
+ for (const [key, value] of Object.entries(metadata)) {
298
+ if (value === undefined)
299
+ continue;
300
+ const result = await runTool("plutil", ["-replace", key, typeof value === "boolean" ? "-bool" : "-string", String(value), join2(contents, "Info.plist")]);
301
+ if (!result.success)
302
+ throw new Error(result.error);
303
+ }
281
304
  }
282
- } else if (opts.appStore) {
283
- results.push({
284
- success: false,
285
- platform: "macos",
286
- format: "pkg",
287
- error: 'App Store packaging requires macos.signIdentity (a "3rd Party Mac Developer Application" identity)'
288
- });
289
- return results;
305
+ if (opts.signIdentity) {
306
+ await signMacOSAppBundle(appBundlePath, bundleId, opts, hardenedRuntime);
307
+ } else if (opts.appStore) {
308
+ throw new Error('App Store packaging requires macos.signIdentity (a "3rd Party Mac Developer Application" identity)');
309
+ }
310
+ } catch (error) {
311
+ return [{ success: false, platform: "macos", format: "app", error: error.message }];
290
312
  }
291
313
  results.push({ success: true, platform: "macos", format: "app", outputPath: appBundlePath });
292
314
  if (opts.dmg !== false) {
@@ -310,7 +332,8 @@ async function packageMacOS(config, outDir) {
310
332
  identifier: bundleId,
311
333
  version,
312
334
  appStore: opts.appStore === true,
313
- installerIdentity: opts.installerIdentity
335
+ installerIdentity: opts.installerIdentity,
336
+ signingKeychain: opts.signingKeychain
314
337
  });
315
338
  results.push({
316
339
  success: pkgResult.success,
@@ -327,19 +350,21 @@ async function packageMacOS(config, outDir) {
327
350
  async function notarizeIfRequested(opts, artifactPath, format) {
328
351
  if (!opts.notarize)
329
352
  return [];
330
- if (!opts.appleId || !opts.applePassword || !opts.teamId) {
353
+ if (!opts.notaryKeychainProfile && !opts.notaryApiKey && (!opts.appleId || !opts.applePassword || !opts.teamId)) {
331
354
  return [{
332
355
  success: false,
333
356
  platform: "macos",
334
357
  format: `${format} (notarize)`,
335
- error: "Notarization requires macos.appleId, macos.applePassword and macos.teamId"
358
+ error: "Notarization requires a keychain profile, API key, or Apple ID/password/team ID"
336
359
  }];
337
360
  }
338
361
  const submitted = await runTool("xcrun", notarytoolArguments({
339
362
  artifactPath,
340
363
  appleId: opts.appleId,
341
364
  applePassword: opts.applePassword,
342
- teamId: opts.teamId
365
+ teamId: opts.teamId,
366
+ keychainProfile: opts.notaryKeychainProfile,
367
+ apiKey: opts.notaryApiKey
343
368
  }));
344
369
  if (!submitted.success)
345
370
  return [{ success: false, platform: "macos", format: `${format} (notarize)`, error: submitted.error }];
@@ -548,6 +573,59 @@ ${body}
548
573
  </dict>
549
574
  </plist>`;
550
575
  }
576
+ async function signMacOSAppBundle(app, bundleId, opts, hardenedRuntime) {
577
+ const scratch = createTempDir("craft-sign-");
578
+ try {
579
+ let helperEntitlements = opts.helperEntitlements;
580
+ if (opts.appStore && !helperEntitlements) {
581
+ helperEntitlements = join2(scratch, "inherit.plist");
582
+ writeFileSync3(helperEntitlements, '<?xml version="1.0"?><plist version="1.0"><dict><key>com.apple.security.app-sandbox</key><true/><key>com.apple.security.inherit</key><true/></dict></plist>');
583
+ }
584
+ const extracted = await runTool("plutil", ["-extract", "CFBundleExecutable", "raw", "-o", "-", join2(app, "Contents", "Info.plist")]);
585
+ if (!extracted.success)
586
+ throw new Error(extracted.error);
587
+ const main = join2(app, "Contents", "MacOS", extracted.stdout.trim());
588
+ const sign = async (path, entitlements, identifier) => {
589
+ const signed = await runTool("codesign", codesignArguments({ path, identity: opts.signIdentity, entitlements, hardenedRuntime, identifier, keychain: opts.signingKeychain, deep: false }));
590
+ if (!signed.success)
591
+ throw new Error(signed.error);
592
+ };
593
+ const walk = async (directory) => {
594
+ for (const entry of readdirSync2(directory)) {
595
+ const path = join2(directory, entry);
596
+ const stat = lstatSync(path);
597
+ if (stat.isSymbolicLink())
598
+ continue;
599
+ if (stat.isDirectory()) {
600
+ await walk(path);
601
+ if (/\.(?:app|framework|xpc|appex|bundle)$/.test(entry))
602
+ await sign(path, /\.(?:app|xpc|appex)$/.test(entry) ? helperEntitlements : undefined);
603
+ } else if (path !== main && stat.isFile() && isMachO(path)) {
604
+ const executable = (stat.mode & 73) !== 0 && !/\.(?:dylib|so)$/.test(entry);
605
+ await sign(path, executable ? helperEntitlements : undefined, `${bundleId}.${entry}`);
606
+ }
607
+ }
608
+ };
609
+ await walk(join2(app, "Contents"));
610
+ await sign(app, opts.entitlements);
611
+ const verified = await runTool("codesign", ["--verify", "--deep", "--strict", app]);
612
+ if (!verified.success)
613
+ throw new Error(verified.error);
614
+ } finally {
615
+ removeTempDir(scratch);
616
+ }
617
+ }
618
+ function isMachO(path) {
619
+ const fd = openSync(path, "r");
620
+ try {
621
+ const magic = Buffer.alloc(4);
622
+ if (readSync(fd, magic, 0, 4, 0) !== 4)
623
+ return false;
624
+ return [4277009102, 3472551422, 4277009103, 3489328638, 3405691582, 3199925962, 3405691583, 3216703178].includes(magic.readUInt32BE());
625
+ } finally {
626
+ closeSync(fd);
627
+ }
628
+ }
551
629
  function codesignArguments(opts) {
552
630
  return [
553
631
  "--force",
@@ -556,7 +634,9 @@ function codesignArguments(opts) {
556
634
  ...opts.identity === "-" ? [] : ["--timestamp"],
557
635
  ...opts.hardenedRuntime ? ["--options", "runtime"] : [],
558
636
  ...opts.entitlements ? ["--entitlements", opts.entitlements] : [],
559
- "--deep",
637
+ ...opts.identifier ? ["--identifier", opts.identifier] : [],
638
+ ...opts.keychain ? ["--keychain", opts.keychain] : [],
639
+ ...opts.deep === false ? [] : ["--deep"],
560
640
  opts.path
561
641
  ];
562
642
  }
@@ -567,10 +647,18 @@ function productbuildArguments(opts) {
567
647
  "/Applications",
568
648
  "--sign",
569
649
  opts.identity,
650
+ ...opts.keychain ? ["--keychain", opts.keychain] : [],
570
651
  opts.outputPath
571
652
  ];
572
653
  }
573
654
  function notarytoolArguments(opts) {
655
+ const base = ["notarytool", "submit", opts.artifactPath, "--wait"];
656
+ if (opts.keychainProfile)
657
+ return [...base, "--keychain-profile", opts.keychainProfile];
658
+ if (opts.apiKey)
659
+ return [...base, "--key", opts.apiKey.path, "--key-id", opts.apiKey.keyId, "--issuer", opts.apiKey.issuer];
660
+ if (!opts.appleId || !opts.applePassword || !opts.teamId)
661
+ throw new Error("Notarization credentials are incomplete");
574
662
  return [
575
663
  "notarytool",
576
664
  "submit",
@@ -584,6 +672,20 @@ function notarytoolArguments(opts) {
584
672
  "--wait"
585
673
  ];
586
674
  }
675
+ function copyMacOSAppBundle(source, destination) {
676
+ try {
677
+ const info = join2(source, "Contents", "Info.plist");
678
+ if (!existsSync4(info) || !existsSync4(join2(source, "Contents", "MacOS")))
679
+ throw new Error(`Invalid prebuilt macOS application: ${source}`);
680
+ if (resolve(source) !== resolve(destination)) {
681
+ rmSync2(destination, { recursive: true, force: true });
682
+ cpSync(source, destination, { recursive: true });
683
+ }
684
+ return { success: true };
685
+ } catch (error) {
686
+ return { success: false, error: error.message };
687
+ }
688
+ }
587
689
  function createMacOSAppBundle(opts) {
588
690
  try {
589
691
  const { name, binaryPath, iconPath, provisioningProfile, additionalExecutables, outputPath } = opts;
@@ -663,7 +765,7 @@ function runTool(tool, args) {
663
765
  });
664
766
  proc.on("close", (code) => {
665
767
  if (code === 0)
666
- resolve({ success: true });
768
+ resolve({ success: true, stdout });
667
769
  else
668
770
  resolve({ success: false, error: formatPackagingCommandError(tool, code, stdout, stderr) });
669
771
  });
@@ -737,7 +839,8 @@ async function createPKG(opts) {
737
839
  const built = await runTool("productbuild", productbuildArguments({
738
840
  appBundlePath: opts.appBundlePath,
739
841
  outputPath: opts.outputPath,
740
- identity: opts.installerIdentity
842
+ identity: opts.installerIdentity,
843
+ keychain: opts.signingKeychain
741
844
  }));
742
845
  return built.success ? { success: true, outputPath: opts.outputPath } : { success: false, error: built.error };
743
846
  }
@@ -2842,7 +2945,7 @@ function pinCraftNativeDependency(packagePath, craftVersion) {
2842
2945
  `);
2843
2946
  }
2844
2947
  // package.json
2845
- var version = "0.0.276";
2948
+ var version = "0.0.290";
2846
2949
 
2847
2950
  // bin/cli.ts
2848
2951
  var spawnedFrom = process6.env[CRAFT_CLI_SPAWN_MARKER];
package/dist/index.cjs CHANGED
@@ -118,6 +118,7 @@ __export(exports_src, {
118
118
  extractBundle: () => extractBundle,
119
119
  extractBundleFromDmg: () => extractBundleFromDmg,
120
120
  extractBundleFromZip: () => extractBundleFromZip,
121
+ files: () => files,
121
122
  focus: () => focus,
122
123
  focusFilters: () => focusFilters,
123
124
  foregroundService: () => foregroundService,
@@ -373,7 +374,7 @@ function craftBinaryNotFoundMessage(triedPath) {
373
374
  `);
374
375
  }
375
376
  // package.json
376
- var version = "0.0.276";
377
+ var version = "0.0.290";
377
378
 
378
379
  // src/sdk-version.ts
379
380
  var SDK_VERSION = version;
@@ -610,8 +611,10 @@ async function packageMacOS(config, outDir) {
610
611
  const { name, version, bundleId = `com.myapp.${name.toLowerCase()}` } = config;
611
612
  const opts = config.macos || {};
612
613
  const hardenedRuntime = !opts.appStore;
614
+ if (opts.appStore && (!opts.entitlements || !opts.provisioningProfile))
615
+ return [{ success: false, platform: "macos", format: "app", error: "App Store packaging requires macos.entitlements and macos.provisioningProfile" }];
613
616
  const appBundlePath = import_path.join(outDir, `${name}.app`);
614
- const appBundle = createMacOSAppBundle({
617
+ const appBundle = opts.appBundlePath ? copyMacOSAppBundle(opts.appBundlePath, appBundlePath) : createMacOSAppBundle({
615
618
  name,
616
619
  version,
617
620
  bundleId,
@@ -635,25 +638,42 @@ async function packageMacOS(config, outDir) {
635
638
  });
636
639
  return results;
637
640
  }
638
- if (opts.signIdentity) {
639
- const signed = await runTool("codesign", codesignArguments({
640
- path: appBundlePath,
641
- identity: opts.signIdentity,
642
- entitlements: opts.entitlements,
643
- hardenedRuntime
644
- }));
645
- if (!signed.success) {
646
- results.push({ success: false, platform: "macos", format: "app", error: signed.error });
647
- return results;
641
+ try {
642
+ const contents = import_path.join(appBundlePath, "Contents");
643
+ if (opts.provisioningProfile) {
644
+ const profile = import_path.join(contents, "embedded.provisionprofile");
645
+ import_fs.copyFileSync(opts.provisioningProfile, profile);
646
+ import_fs.chmodSync(profile, 420);
647
+ }
648
+ if (opts.privacyManifest)
649
+ import_fs.copyFileSync(opts.privacyManifest, import_path.join(contents, "Resources", "PrivacyInfo.xcprivacy"));
650
+ if (opts.appBundlePath) {
651
+ const metadata = {
652
+ CFBundleVersion: opts.buildNumber,
653
+ LSApplicationCategoryType: opts.category,
654
+ LSMinimumSystemVersion: opts.minimumSystemVersion,
655
+ LSUIElement: opts.menuBarOnly
656
+ };
657
+ if (config.iconPath) {
658
+ const icon = import_path.basename(config.iconPath);
659
+ import_fs.copyFileSync(config.iconPath, import_path.join(contents, "Resources", icon));
660
+ metadata.CFBundleIconFile = icon;
661
+ }
662
+ for (const [key, value] of Object.entries(metadata)) {
663
+ if (value === undefined)
664
+ continue;
665
+ const result = await runTool("plutil", ["-replace", key, typeof value === "boolean" ? "-bool" : "-string", String(value), import_path.join(contents, "Info.plist")]);
666
+ if (!result.success)
667
+ throw new Error(result.error);
668
+ }
648
669
  }
649
- } else if (opts.appStore) {
650
- results.push({
651
- success: false,
652
- platform: "macos",
653
- format: "pkg",
654
- error: 'App Store packaging requires macos.signIdentity (a "3rd Party Mac Developer Application" identity)'
655
- });
656
- return results;
670
+ if (opts.signIdentity) {
671
+ await signMacOSAppBundle(appBundlePath, bundleId, opts, hardenedRuntime);
672
+ } else if (opts.appStore) {
673
+ throw new Error('App Store packaging requires macos.signIdentity (a "3rd Party Mac Developer Application" identity)');
674
+ }
675
+ } catch (error) {
676
+ return [{ success: false, platform: "macos", format: "app", error: error.message }];
657
677
  }
658
678
  results.push({ success: true, platform: "macos", format: "app", outputPath: appBundlePath });
659
679
  if (opts.dmg !== false) {
@@ -677,7 +697,8 @@ async function packageMacOS(config, outDir) {
677
697
  identifier: bundleId,
678
698
  version,
679
699
  appStore: opts.appStore === true,
680
- installerIdentity: opts.installerIdentity
700
+ installerIdentity: opts.installerIdentity,
701
+ signingKeychain: opts.signingKeychain
681
702
  });
682
703
  results.push({
683
704
  success: pkgResult.success,
@@ -694,19 +715,21 @@ async function packageMacOS(config, outDir) {
694
715
  async function notarizeIfRequested(opts, artifactPath, format) {
695
716
  if (!opts.notarize)
696
717
  return [];
697
- if (!opts.appleId || !opts.applePassword || !opts.teamId) {
718
+ if (!opts.notaryKeychainProfile && !opts.notaryApiKey && (!opts.appleId || !opts.applePassword || !opts.teamId)) {
698
719
  return [{
699
720
  success: false,
700
721
  platform: "macos",
701
722
  format: `${format} (notarize)`,
702
- error: "Notarization requires macos.appleId, macos.applePassword and macos.teamId"
723
+ error: "Notarization requires a keychain profile, API key, or Apple ID/password/team ID"
703
724
  }];
704
725
  }
705
726
  const submitted = await runTool("xcrun", notarytoolArguments({
706
727
  artifactPath,
707
728
  appleId: opts.appleId,
708
729
  applePassword: opts.applePassword,
709
- teamId: opts.teamId
730
+ teamId: opts.teamId,
731
+ keychainProfile: opts.notaryKeychainProfile,
732
+ apiKey: opts.notaryApiKey
710
733
  }));
711
734
  if (!submitted.success)
712
735
  return [{ success: false, platform: "macos", format: `${format} (notarize)`, error: submitted.error }];
@@ -916,6 +939,59 @@ ${body}
916
939
  </dict>
917
940
  </plist>`;
918
941
  }
942
+ async function signMacOSAppBundle(app, bundleId, opts, hardenedRuntime) {
943
+ const scratch = createTempDir("craft-sign-");
944
+ try {
945
+ let helperEntitlements = opts.helperEntitlements;
946
+ if (opts.appStore && !helperEntitlements) {
947
+ helperEntitlements = import_path.join(scratch, "inherit.plist");
948
+ import_fs.writeFileSync(helperEntitlements, '<?xml version="1.0"?><plist version="1.0"><dict><key>com.apple.security.app-sandbox</key><true/><key>com.apple.security.inherit</key><true/></dict></plist>');
949
+ }
950
+ const extracted = await runTool("plutil", ["-extract", "CFBundleExecutable", "raw", "-o", "-", import_path.join(app, "Contents", "Info.plist")]);
951
+ if (!extracted.success)
952
+ throw new Error(extracted.error);
953
+ const main = import_path.join(app, "Contents", "MacOS", extracted.stdout.trim());
954
+ const sign = async (path, entitlements, identifier) => {
955
+ const signed = await runTool("codesign", codesignArguments({ path, identity: opts.signIdentity, entitlements, hardenedRuntime, identifier, keychain: opts.signingKeychain, deep: false }));
956
+ if (!signed.success)
957
+ throw new Error(signed.error);
958
+ };
959
+ const walk = async (directory) => {
960
+ for (const entry of import_fs.readdirSync(directory)) {
961
+ const path = import_path.join(directory, entry);
962
+ const stat = import_fs.lstatSync(path);
963
+ if (stat.isSymbolicLink())
964
+ continue;
965
+ if (stat.isDirectory()) {
966
+ await walk(path);
967
+ if (/\.(?:app|framework|xpc|appex|bundle)$/.test(entry))
968
+ await sign(path, /\.(?:app|xpc|appex)$/.test(entry) ? helperEntitlements : undefined);
969
+ } else if (path !== main && stat.isFile() && isMachO(path)) {
970
+ const executable = (stat.mode & 73) !== 0 && !/\.(?:dylib|so)$/.test(entry);
971
+ await sign(path, executable ? helperEntitlements : undefined, `${bundleId}.${entry}`);
972
+ }
973
+ }
974
+ };
975
+ await walk(import_path.join(app, "Contents"));
976
+ await sign(app, opts.entitlements);
977
+ const verified = await runTool("codesign", ["--verify", "--deep", "--strict", app]);
978
+ if (!verified.success)
979
+ throw new Error(verified.error);
980
+ } finally {
981
+ removeTempDir(scratch);
982
+ }
983
+ }
984
+ function isMachO(path) {
985
+ const fd = import_fs.openSync(path, "r");
986
+ try {
987
+ const magic = Buffer.alloc(4);
988
+ if (import_fs.readSync(fd, magic, 0, 4, 0) !== 4)
989
+ return false;
990
+ return [4277009102, 3472551422, 4277009103, 3489328638, 3405691582, 3199925962, 3405691583, 3216703178].includes(magic.readUInt32BE());
991
+ } finally {
992
+ import_fs.closeSync(fd);
993
+ }
994
+ }
919
995
  function codesignArguments(opts) {
920
996
  return [
921
997
  "--force",
@@ -924,7 +1000,9 @@ function codesignArguments(opts) {
924
1000
  ...opts.identity === "-" ? [] : ["--timestamp"],
925
1001
  ...opts.hardenedRuntime ? ["--options", "runtime"] : [],
926
1002
  ...opts.entitlements ? ["--entitlements", opts.entitlements] : [],
927
- "--deep",
1003
+ ...opts.identifier ? ["--identifier", opts.identifier] : [],
1004
+ ...opts.keychain ? ["--keychain", opts.keychain] : [],
1005
+ ...opts.deep === false ? [] : ["--deep"],
928
1006
  opts.path
929
1007
  ];
930
1008
  }
@@ -935,10 +1013,18 @@ function productbuildArguments(opts) {
935
1013
  "/Applications",
936
1014
  "--sign",
937
1015
  opts.identity,
1016
+ ...opts.keychain ? ["--keychain", opts.keychain] : [],
938
1017
  opts.outputPath
939
1018
  ];
940
1019
  }
941
1020
  function notarytoolArguments(opts) {
1021
+ const base = ["notarytool", "submit", opts.artifactPath, "--wait"];
1022
+ if (opts.keychainProfile)
1023
+ return [...base, "--keychain-profile", opts.keychainProfile];
1024
+ if (opts.apiKey)
1025
+ return [...base, "--key", opts.apiKey.path, "--key-id", opts.apiKey.keyId, "--issuer", opts.apiKey.issuer];
1026
+ if (!opts.appleId || !opts.applePassword || !opts.teamId)
1027
+ throw new Error("Notarization credentials are incomplete");
942
1028
  return [
943
1029
  "notarytool",
944
1030
  "submit",
@@ -952,6 +1038,20 @@ function notarytoolArguments(opts) {
952
1038
  "--wait"
953
1039
  ];
954
1040
  }
1041
+ function copyMacOSAppBundle(source, destination) {
1042
+ try {
1043
+ const info = import_path.join(source, "Contents", "Info.plist");
1044
+ if (!import_fs.existsSync(info) || !import_fs.existsSync(import_path.join(source, "Contents", "MacOS")))
1045
+ throw new Error(`Invalid prebuilt macOS application: ${source}`);
1046
+ if (import_path.resolve(source) !== import_path.resolve(destination)) {
1047
+ import_fs.rmSync(destination, { recursive: true, force: true });
1048
+ import_fs.cpSync(source, destination, { recursive: true });
1049
+ }
1050
+ return { success: true };
1051
+ } catch (error) {
1052
+ return { success: false, error: error.message };
1053
+ }
1054
+ }
955
1055
  function createMacOSAppBundle(opts) {
956
1056
  try {
957
1057
  const { name, binaryPath, iconPath, provisioningProfile, additionalExecutables, outputPath } = opts;
@@ -1033,7 +1133,7 @@ function runTool(tool, args) {
1033
1133
  });
1034
1134
  proc.on("close", (code) => {
1035
1135
  if (code === 0)
1036
- resolve({ success: true });
1136
+ resolve({ success: true, stdout });
1037
1137
  else
1038
1138
  resolve({ success: false, error: formatPackagingCommandError(tool, code, stdout, stderr) });
1039
1139
  });
@@ -1108,7 +1208,8 @@ async function createPKG(opts) {
1108
1208
  const built = await runTool("productbuild", productbuildArguments({
1109
1209
  appBundlePath: opts.appBundlePath,
1110
1210
  outputPath: opts.outputPath,
1111
- identity: opts.installerIdentity
1211
+ identity: opts.installerIdentity,
1212
+ keychain: opts.signingKeychain
1112
1213
  }));
1113
1214
  return built.success ? { success: true, outputPath: opts.outputPath } : { success: false, error: built.error };
1114
1215
  }
@@ -5106,6 +5207,55 @@ var permissions = {
5106
5207
  }
5107
5208
  }
5108
5209
  };
5210
+ var files = {
5211
+ async pickFile(options = {}) {
5212
+ const craft = getCraftMobile();
5213
+ if (typeof window !== "undefined" && craft?.files) {
5214
+ return craft.files.pickFile(options);
5215
+ }
5216
+ if (typeof document === "undefined") {
5217
+ throw new Error("File picker not available. Must run in Craft or a browser.");
5218
+ }
5219
+ return new Promise((resolve, reject) => {
5220
+ const input = document.createElement("input");
5221
+ input.type = "file";
5222
+ if (options.contentTypes?.length)
5223
+ input.accept = options.contentTypes.join(",");
5224
+ const requested = Number(options.maxSelections);
5225
+ const maxSelections = Number.isFinite(requested) && requested > 0 ? Math.min(20, Math.floor(requested)) : 1;
5226
+ input.multiple = maxSelections > 1;
5227
+ input.style.display = "none";
5228
+ let settled = false;
5229
+ const finish = (result) => {
5230
+ if (settled)
5231
+ return;
5232
+ settled = true;
5233
+ input.remove();
5234
+ result();
5235
+ };
5236
+ input.addEventListener("cancel", () => finish(() => resolve([])), { once: true });
5237
+ input.onchange = () => {
5238
+ const chosen = Array.from(input.files ?? []).slice(0, maxSelections);
5239
+ const limit = Number(options.maxBytes);
5240
+ if (Number.isFinite(limit) && limit > 0) {
5241
+ const oversized = chosen.find((file) => file.size > limit);
5242
+ if (oversized) {
5243
+ finish(() => reject(Object.assign(new Error("The selected file is too large"), { code: "FILE_TOO_LARGE" })));
5244
+ return;
5245
+ }
5246
+ }
5247
+ finish(() => resolve(chosen.map((file) => ({
5248
+ path: URL.createObjectURL(file),
5249
+ name: file.name,
5250
+ type: file.type || "application/octet-stream",
5251
+ size: file.size
5252
+ }))));
5253
+ };
5254
+ document.body.append(input);
5255
+ input.click();
5256
+ });
5257
+ }
5258
+ };
5109
5259
  var camera = {
5110
5260
  async takePicture(options = {}) {
5111
5261
  const craft = getCraftMobile();
package/dist/index.d.cts CHANGED
@@ -85,4 +85,4 @@ export declare function show(html: string, options?: WindowOptions): Promise<voi
85
85
  * Quick helper to load a URL
86
86
  */
87
87
  export declare function loadURL(url: string, options?: WindowOptions): Promise<void>;
88
- export type { WindowOptions, CraftSidebarAPI, CraftNativeUIAPI, CraftContextMenuItem, CraftContextMenuResult, AppConfig, CraftTrayAPI, CraftWindowAPI, CraftAppAPI, CraftBridgeAPI, Permission, HapticType, CraftHapticsAPI, CameraOptions, PhotoPickerOptions, CraftMobileAPI, CraftFileSystemAPI, CraftFsWatchEvent, CraftFsWatchHandle, CraftDatabaseAPI, CraftHttpAPI, CraftCryptoAPI, CraftCapabilities, CapabilityNamespace, CapabilityNamespaceStatus, CapabilityAction, CraftPreferencesAPI, CraftPreferencesInfo, CraftSettingsAPI, PreferenceValue, CraftEventType, CraftEventMap, CraftEventHandler, CraftEventEmitter, IOSConfig, AndroidConfig, MacOSConfig, WindowsConfig, LinuxConfig, CraftAppConfig, } from './types.js';
88
+ export type { WindowOptions, CraftSidebarAPI, CraftNativeUIAPI, CraftContextMenuItem, CraftContextMenuResult, AppConfig, CraftTrayAPI, CraftWindowAPI, CraftAppAPI, CraftBridgeAPI, CraftTimersAPI, Permission, HapticType, CraftHapticsAPI, CameraOptions, PhotoPickerOptions, CraftMobileAPI, CraftFileSystemAPI, CraftFsWatchEvent, CraftFsWatchHandle, CraftDatabaseAPI, CraftHttpAPI, CraftCryptoAPI, CraftCapabilities, CapabilityNamespace, CapabilityNamespaceStatus, CapabilityAction, CraftPreferencesAPI, CraftPreferencesInfo, CraftSettingsAPI, PreferenceValue, CraftEventType, CraftEventMap, CraftEventHandler, CraftEventEmitter, IOSConfig, AndroidConfig, MacOSConfig, WindowsConfig, LinuxConfig, CraftAppConfig, } from './types.js';
package/dist/index.d.ts CHANGED
@@ -85,4 +85,4 @@ export declare function show(html: string, options?: WindowOptions): Promise<voi
85
85
  * Quick helper to load a URL
86
86
  */
87
87
  export declare function loadURL(url: string, options?: WindowOptions): Promise<void>;
88
- export type { WindowOptions, CraftSidebarAPI, CraftNativeUIAPI, CraftContextMenuItem, CraftContextMenuResult, AppConfig, CraftTrayAPI, CraftWindowAPI, CraftAppAPI, CraftBridgeAPI, Permission, HapticType, CraftHapticsAPI, CameraOptions, PhotoPickerOptions, CraftMobileAPI, CraftFileSystemAPI, CraftFsWatchEvent, CraftFsWatchHandle, CraftDatabaseAPI, CraftHttpAPI, CraftCryptoAPI, CraftCapabilities, CapabilityNamespace, CapabilityNamespaceStatus, CapabilityAction, CraftPreferencesAPI, CraftPreferencesInfo, CraftSettingsAPI, PreferenceValue, CraftEventType, CraftEventMap, CraftEventHandler, CraftEventEmitter, IOSConfig, AndroidConfig, MacOSConfig, WindowsConfig, LinuxConfig, CraftAppConfig, } from './types.js';
88
+ export type { WindowOptions, CraftSidebarAPI, CraftNativeUIAPI, CraftContextMenuItem, CraftContextMenuResult, AppConfig, CraftTrayAPI, CraftWindowAPI, CraftAppAPI, CraftBridgeAPI, CraftTimersAPI, Permission, HapticType, CraftHapticsAPI, CameraOptions, PhotoPickerOptions, CraftMobileAPI, CraftFileSystemAPI, CraftFsWatchEvent, CraftFsWatchHandle, CraftDatabaseAPI, CraftHttpAPI, CraftCryptoAPI, CraftCapabilities, CapabilityNamespace, CapabilityNamespaceStatus, CapabilityAction, CraftPreferencesAPI, CraftPreferencesInfo, CraftSettingsAPI, PreferenceValue, CraftEventType, CraftEventMap, CraftEventHandler, CraftEventEmitter, IOSConfig, AndroidConfig, MacOSConfig, WindowsConfig, LinuxConfig, CraftAppConfig, } from './types.js';