craft-native 0.0.75 → 0.0.77
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 +244 -125
- package/dist/index.cjs +98 -17
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +98 -17
- package/dist/package.d.ts +69 -0
- package/dist/types.d.ts +294 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -22,9 +22,12 @@ var exports_package = {};
|
|
|
22
22
|
__export(exports_package, {
|
|
23
23
|
windowsExecutableName: () => windowsExecutableName,
|
|
24
24
|
windowsArchitecture: () => windowsArchitecture,
|
|
25
|
+
urlTypesEntry: () => urlTypesEntry,
|
|
25
26
|
shouldRetryHdiutil: () => shouldRetryHdiutil,
|
|
26
27
|
renderWixSource: () => renderWixSource,
|
|
27
28
|
productbuildArguments: () => productbuildArguments,
|
|
29
|
+
pkgbuildComponentPlist: () => pkgbuildComponentPlist,
|
|
30
|
+
pkgbuildArguments: () => pkgbuildArguments,
|
|
28
31
|
packageApp: () => packageApp,
|
|
29
32
|
pack: () => pack,
|
|
30
33
|
notarytoolArguments: () => notarytoolArguments,
|
|
@@ -162,6 +165,7 @@ async function packageMacOS(config, outDir) {
|
|
|
162
165
|
category: opts.category,
|
|
163
166
|
minimumSystemVersion: opts.minimumSystemVersion,
|
|
164
167
|
menuBarOnly: opts.menuBarOnly,
|
|
168
|
+
urlSchemes: opts.urlSchemes,
|
|
165
169
|
binaryPath: config.binaryPath,
|
|
166
170
|
iconPath: config.iconPath,
|
|
167
171
|
provisioningProfile: opts.provisioningProfile,
|
|
@@ -358,6 +362,36 @@ async function packageLinux(config, outDir) {
|
|
|
358
362
|
}
|
|
359
363
|
return results;
|
|
360
364
|
}
|
|
365
|
+
function urlTypesEntry(bundleId, schemes) {
|
|
366
|
+
const seen = new Set;
|
|
367
|
+
const unique = [];
|
|
368
|
+
for (const scheme of schemes) {
|
|
369
|
+
if (!URL_SCHEME.test(scheme))
|
|
370
|
+
throw new Error(`Invalid URL scheme ${JSON.stringify(scheme)}: must start with a letter and contain only letters, digits, "+", "-" or "."`);
|
|
371
|
+
const key = scheme.toLowerCase();
|
|
372
|
+
if (seen.has(key))
|
|
373
|
+
continue;
|
|
374
|
+
seen.add(key);
|
|
375
|
+
unique.push(scheme);
|
|
376
|
+
}
|
|
377
|
+
if (unique.length === 0)
|
|
378
|
+
return null;
|
|
379
|
+
const list = unique.map((scheme) => ` <string>${xml(scheme)}</string>`).join(`
|
|
380
|
+
`);
|
|
381
|
+
return ` <key>CFBundleURLTypes</key>
|
|
382
|
+
<array>
|
|
383
|
+
<dict>
|
|
384
|
+
<key>CFBundleURLName</key>
|
|
385
|
+
<string>${xml(bundleId)}</string>
|
|
386
|
+
<key>CFBundleTypeRole</key>
|
|
387
|
+
<string>Viewer</string>
|
|
388
|
+
<key>CFBundleURLSchemes</key>
|
|
389
|
+
<array>
|
|
390
|
+
${list}
|
|
391
|
+
</array>
|
|
392
|
+
</dict>
|
|
393
|
+
</array>`;
|
|
394
|
+
}
|
|
361
395
|
function macOSInfoPlist(metadata) {
|
|
362
396
|
const entries = [
|
|
363
397
|
["CFBundleExecutable", metadata.name],
|
|
@@ -376,9 +410,12 @@ function macOSInfoPlist(metadata) {
|
|
|
376
410
|
entries.push(["LSMinimumSystemVersion", metadata.minimumSystemVersion]);
|
|
377
411
|
if (metadata.menuBarOnly)
|
|
378
412
|
entries.push(["LSUIElement", true]);
|
|
379
|
-
const
|
|
413
|
+
const scalars = entries.map(([key, value]) => ` <key>${xml(key)}</key>
|
|
380
414
|
${value === true ? " <true/>" : ` <string>${xml(value)}</string>`}`).join(`
|
|
381
415
|
`);
|
|
416
|
+
const urlTypes = urlTypesEntry(metadata.bundleId, metadata.urlSchemes || []);
|
|
417
|
+
const body = urlTypes ? `${scalars}
|
|
418
|
+
${urlTypes}` : scalars;
|
|
382
419
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
383
420
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
384
421
|
<plist version="1.0">
|
|
@@ -528,6 +565,43 @@ async function createDMG(opts) {
|
|
|
528
565
|
return { success: false, error: failures.join(`
|
|
529
566
|
`) };
|
|
530
567
|
}
|
|
568
|
+
function pkgbuildComponentPlist(rootRelativeBundlePath) {
|
|
569
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
570
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
571
|
+
<plist version="1.0">
|
|
572
|
+
<array>
|
|
573
|
+
<dict>
|
|
574
|
+
<key>BundleHasStrictIdentifier</key>
|
|
575
|
+
<true/>
|
|
576
|
+
<key>BundleIsRelocatable</key>
|
|
577
|
+
<false/>
|
|
578
|
+
<key>BundleIsVersionChecked</key>
|
|
579
|
+
<false/>
|
|
580
|
+
<key>BundleOverwriteAction</key>
|
|
581
|
+
<string>upgrade</string>
|
|
582
|
+
<key>RootRelativeBundlePath</key>
|
|
583
|
+
<string>${xml(rootRelativeBundlePath)}</string>
|
|
584
|
+
</dict>
|
|
585
|
+
</array>
|
|
586
|
+
</plist>
|
|
587
|
+
`;
|
|
588
|
+
}
|
|
589
|
+
function pkgbuildArguments(opts) {
|
|
590
|
+
return [
|
|
591
|
+
"--root",
|
|
592
|
+
opts.root,
|
|
593
|
+
"--component-plist",
|
|
594
|
+
opts.componentPlistPath,
|
|
595
|
+
"--identifier",
|
|
596
|
+
opts.identifier,
|
|
597
|
+
"--version",
|
|
598
|
+
opts.version,
|
|
599
|
+
"--install-location",
|
|
600
|
+
"/",
|
|
601
|
+
...opts.installerIdentity ? ["--sign", opts.installerIdentity] : [],
|
|
602
|
+
opts.outputPath
|
|
603
|
+
];
|
|
604
|
+
}
|
|
531
605
|
async function createPKG(opts) {
|
|
532
606
|
if (!/^[a-zA-Z0-9._-]+$/.test(opts.identifier) || !opts.identifier.includes("."))
|
|
533
607
|
return { success: false, error: `Invalid pkg identifier "${opts.identifier}"; expected reverse-DNS like com.example.app` };
|
|
@@ -545,21 +619,22 @@ async function createPKG(opts) {
|
|
|
545
619
|
}
|
|
546
620
|
const tempDir = mkdtempSync(join(tmpdir(), "craft-pkg-"));
|
|
547
621
|
try {
|
|
548
|
-
const
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
opts.outputPath
|
|
562
|
-
|
|
622
|
+
const root = join(tempDir, "root");
|
|
623
|
+
const appsDir = join(root, "Applications");
|
|
624
|
+
mkdirSync(appsDir, { recursive: true, mode: 493 });
|
|
625
|
+
chmodSync(root, 493);
|
|
626
|
+
const bundleName = basename(opts.appBundlePath);
|
|
627
|
+
cpSync(opts.appBundlePath, join(appsDir, bundleName), { recursive: true });
|
|
628
|
+
const componentPlistPath = join(tempDir, "component.plist");
|
|
629
|
+
writeFileSync(componentPlistPath, pkgbuildComponentPlist(`Applications/${bundleName}`));
|
|
630
|
+
const built = await runTool("pkgbuild", pkgbuildArguments({
|
|
631
|
+
root,
|
|
632
|
+
componentPlistPath,
|
|
633
|
+
identifier: opts.identifier,
|
|
634
|
+
version: opts.version,
|
|
635
|
+
outputPath: opts.outputPath,
|
|
636
|
+
installerIdentity: opts.installerIdentity
|
|
637
|
+
}));
|
|
563
638
|
return built.success ? { success: true, outputPath: opts.outputPath } : { success: false, error: built.error };
|
|
564
639
|
} finally {
|
|
565
640
|
rmSync(tempDir, { recursive: true, force: true });
|
|
@@ -956,7 +1031,7 @@ async function pack(options) {
|
|
|
956
1031
|
platforms: [detectPlatform()]
|
|
957
1032
|
});
|
|
958
1033
|
}
|
|
959
|
-
var CRC32_TABLE, MEBIBYTE, HDIUTIL_MAX_ATTEMPTS = 3, runHdiutil = (args) => runTool("hdiutil", args);
|
|
1034
|
+
var CRC32_TABLE, URL_SCHEME, MEBIBYTE, HDIUTIL_MAX_ATTEMPTS = 3, runHdiutil = (args) => runTool("hdiutil", args);
|
|
960
1035
|
var init_package = __esm(() => {
|
|
961
1036
|
CRC32_TABLE = (() => {
|
|
962
1037
|
const table = new Uint32Array(256);
|
|
@@ -968,6 +1043,7 @@ var init_package = __esm(() => {
|
|
|
968
1043
|
}
|
|
969
1044
|
return table;
|
|
970
1045
|
})();
|
|
1046
|
+
URL_SCHEME = /^[a-z][a-z0-9+.-]*$/i;
|
|
971
1047
|
MEBIBYTE = 1024 * 1024;
|
|
972
1048
|
});
|
|
973
1049
|
|
|
@@ -2124,90 +2200,6 @@ class Telemetry {
|
|
|
2124
2200
|
}
|
|
2125
2201
|
}
|
|
2126
2202
|
var telemetry = new Telemetry;
|
|
2127
|
-
function parseArgv(argv, opts = {}) {
|
|
2128
|
-
const result = { _: [] };
|
|
2129
|
-
const alias = opts.alias || {};
|
|
2130
|
-
const booleans = new Set(opts.boolean || []);
|
|
2131
|
-
const aliasOf = {};
|
|
2132
|
-
for (const key of Object.keys(alias)) {
|
|
2133
|
-
for (const a of alias[key]) {
|
|
2134
|
-
aliasOf[a] = key;
|
|
2135
|
-
}
|
|
2136
|
-
}
|
|
2137
|
-
for (const b of booleans) {
|
|
2138
|
-
if (alias[b]) {
|
|
2139
|
-
for (const a of alias[b])
|
|
2140
|
-
booleans.add(a);
|
|
2141
|
-
}
|
|
2142
|
-
}
|
|
2143
|
-
function setKey(key, value) {
|
|
2144
|
-
const canonical = aliasOf[key] || key;
|
|
2145
|
-
result[canonical] = value;
|
|
2146
|
-
if (alias[canonical]) {
|
|
2147
|
-
for (const a of alias[canonical])
|
|
2148
|
-
result[a] = value;
|
|
2149
|
-
}
|
|
2150
|
-
if (aliasOf[key] && alias[aliasOf[key]]) {
|
|
2151
|
-
for (const a of alias[aliasOf[key]])
|
|
2152
|
-
result[a] = value;
|
|
2153
|
-
}
|
|
2154
|
-
result[key] = value;
|
|
2155
|
-
}
|
|
2156
|
-
for (let i = 0;i < argv.length; i++) {
|
|
2157
|
-
const arg = argv[i];
|
|
2158
|
-
if (arg === "--") {
|
|
2159
|
-
result._.push(...argv.slice(i + 1));
|
|
2160
|
-
break;
|
|
2161
|
-
}
|
|
2162
|
-
if (arg.startsWith("--")) {
|
|
2163
|
-
const eqIdx = arg.indexOf("=");
|
|
2164
|
-
if (eqIdx !== -1) {
|
|
2165
|
-
const key = arg.slice(2, eqIdx);
|
|
2166
|
-
const value = arg.slice(eqIdx + 1);
|
|
2167
|
-
setKey(key, value);
|
|
2168
|
-
} else {
|
|
2169
|
-
const key = arg.slice(2);
|
|
2170
|
-
if (key.startsWith("no-")) {
|
|
2171
|
-
const actualKey = key.slice(3);
|
|
2172
|
-
setKey(actualKey, false);
|
|
2173
|
-
continue;
|
|
2174
|
-
}
|
|
2175
|
-
const canonical = aliasOf[key] || key;
|
|
2176
|
-
if (booleans.has(canonical) || booleans.has(key)) {
|
|
2177
|
-
setKey(key, true);
|
|
2178
|
-
} else {
|
|
2179
|
-
const next = argv[i + 1];
|
|
2180
|
-
if (next !== undefined && !next.startsWith("-")) {
|
|
2181
|
-
setKey(key, next);
|
|
2182
|
-
i++;
|
|
2183
|
-
} else {
|
|
2184
|
-
setKey(key, true);
|
|
2185
|
-
}
|
|
2186
|
-
}
|
|
2187
|
-
}
|
|
2188
|
-
} else if (arg.startsWith("-") && arg.length > 1) {
|
|
2189
|
-
const chars = arg.slice(1);
|
|
2190
|
-
for (let j = 0;j < chars.length; j++) {
|
|
2191
|
-
const ch = chars[j];
|
|
2192
|
-
const canonical = aliasOf[ch] || ch;
|
|
2193
|
-
if (j === chars.length - 1 && !booleans.has(canonical) && !booleans.has(ch)) {
|
|
2194
|
-
const next = argv[i + 1];
|
|
2195
|
-
if (next !== undefined && !next.startsWith("-")) {
|
|
2196
|
-
setKey(ch, next);
|
|
2197
|
-
i++;
|
|
2198
|
-
} else {
|
|
2199
|
-
setKey(ch, true);
|
|
2200
|
-
}
|
|
2201
|
-
} else {
|
|
2202
|
-
setKey(ch, true);
|
|
2203
|
-
}
|
|
2204
|
-
}
|
|
2205
|
-
} else {
|
|
2206
|
-
result._.push(arg);
|
|
2207
|
-
}
|
|
2208
|
-
}
|
|
2209
|
-
return result;
|
|
2210
|
-
}
|
|
2211
2203
|
function removeBrackets(v) {
|
|
2212
2204
|
return v.replace(/[<[].+/, "").trim();
|
|
2213
2205
|
}
|
|
@@ -2304,9 +2296,13 @@ function camelcaseOptionName(name) {
|
|
|
2304
2296
|
class ClappError extends Error {
|
|
2305
2297
|
exitCode = 2;
|
|
2306
2298
|
isUsageError = true;
|
|
2307
|
-
|
|
2299
|
+
usage;
|
|
2300
|
+
constructor(message, usage) {
|
|
2308
2301
|
super(message);
|
|
2309
2302
|
this.name = this.constructor.name;
|
|
2303
|
+
if (usage !== undefined) {
|
|
2304
|
+
this.usage = usage;
|
|
2305
|
+
}
|
|
2310
2306
|
if (typeof Error.captureStackTrace === "function") {
|
|
2311
2307
|
Error.captureStackTrace(this, this.constructor);
|
|
2312
2308
|
} else {
|
|
@@ -2323,6 +2319,12 @@ ${this.stack}`;
|
|
|
2323
2319
|
return this.message;
|
|
2324
2320
|
}
|
|
2325
2321
|
}
|
|
2322
|
+
function isClappError(err) {
|
|
2323
|
+
if (err instanceof ClappError) {
|
|
2324
|
+
return true;
|
|
2325
|
+
}
|
|
2326
|
+
return !!err && typeof err === "object" && err.name === "ClappError" && typeof err.message === "string";
|
|
2327
|
+
}
|
|
2326
2328
|
function isUnicodeSupported() {
|
|
2327
2329
|
const { env } = process22;
|
|
2328
2330
|
const { TERM, TERM_PROGRAM } = env;
|
|
@@ -2404,6 +2406,99 @@ function levenshteinDistance(a, b) {
|
|
|
2404
2406
|
function findSimilarCommands(input, commands, maxDistance = 2, maxSuggestions = 3) {
|
|
2405
2407
|
return commands.map((cmd) => ({ cmd, distance: levenshteinDistance(input, cmd) })).filter(({ distance }) => distance <= maxDistance).sort((a, b) => a.distance - b.distance).slice(0, maxSuggestions).map(({ cmd }) => cmd);
|
|
2406
2408
|
}
|
|
2409
|
+
function parseArgv(argv, opts = {}) {
|
|
2410
|
+
const result = { _: [] };
|
|
2411
|
+
const alias = opts.alias || {};
|
|
2412
|
+
const booleans = new Set(opts.boolean || []);
|
|
2413
|
+
const aliasOf = {};
|
|
2414
|
+
for (const key of Object.keys(alias)) {
|
|
2415
|
+
for (const a of alias[key]) {
|
|
2416
|
+
aliasOf[a] = key;
|
|
2417
|
+
}
|
|
2418
|
+
}
|
|
2419
|
+
for (const b of booleans) {
|
|
2420
|
+
if (alias[b]) {
|
|
2421
|
+
for (const a of alias[b])
|
|
2422
|
+
booleans.add(a);
|
|
2423
|
+
}
|
|
2424
|
+
}
|
|
2425
|
+
function merge(existing, value) {
|
|
2426
|
+
if (existing === undefined)
|
|
2427
|
+
return value;
|
|
2428
|
+
if (typeof value === "boolean")
|
|
2429
|
+
return value;
|
|
2430
|
+
return Array.isArray(existing) ? [...existing, value] : [existing, value];
|
|
2431
|
+
}
|
|
2432
|
+
function setKey(key, value) {
|
|
2433
|
+
const canonical = aliasOf[key] || key;
|
|
2434
|
+
const merged = merge(result[canonical], value);
|
|
2435
|
+
result[canonical] = merged;
|
|
2436
|
+
if (alias[canonical]) {
|
|
2437
|
+
for (const a of alias[canonical])
|
|
2438
|
+
result[a] = merged;
|
|
2439
|
+
}
|
|
2440
|
+
if (aliasOf[key] && alias[aliasOf[key]]) {
|
|
2441
|
+
for (const a of alias[aliasOf[key]])
|
|
2442
|
+
result[a] = merged;
|
|
2443
|
+
}
|
|
2444
|
+
result[key] = merged;
|
|
2445
|
+
}
|
|
2446
|
+
for (let i = 0;i < argv.length; i++) {
|
|
2447
|
+
const arg = argv[i];
|
|
2448
|
+
if (arg === "--") {
|
|
2449
|
+
result._.push(...argv.slice(i + 1));
|
|
2450
|
+
break;
|
|
2451
|
+
}
|
|
2452
|
+
if (arg.startsWith("--")) {
|
|
2453
|
+
const eqIdx = arg.indexOf("=");
|
|
2454
|
+
if (eqIdx !== -1) {
|
|
2455
|
+
const key = camelcase(arg.slice(2, eqIdx));
|
|
2456
|
+
const value = arg.slice(eqIdx + 1);
|
|
2457
|
+
setKey(key, value);
|
|
2458
|
+
} else {
|
|
2459
|
+
const rawKey = arg.slice(2);
|
|
2460
|
+
if (rawKey.startsWith("no-")) {
|
|
2461
|
+
const actualKey = camelcase(rawKey.slice(3));
|
|
2462
|
+
setKey(actualKey, false);
|
|
2463
|
+
continue;
|
|
2464
|
+
}
|
|
2465
|
+
const key = camelcase(rawKey);
|
|
2466
|
+
const canonical = aliasOf[key] || key;
|
|
2467
|
+
if (booleans.has(canonical) || booleans.has(key)) {
|
|
2468
|
+
setKey(key, true);
|
|
2469
|
+
} else {
|
|
2470
|
+
const next = argv[i + 1];
|
|
2471
|
+
if (next !== undefined && !next.startsWith("-")) {
|
|
2472
|
+
setKey(key, next);
|
|
2473
|
+
i++;
|
|
2474
|
+
} else {
|
|
2475
|
+
setKey(key, true);
|
|
2476
|
+
}
|
|
2477
|
+
}
|
|
2478
|
+
}
|
|
2479
|
+
} else if (arg.startsWith("-") && arg.length > 1) {
|
|
2480
|
+
const chars = arg.slice(1);
|
|
2481
|
+
for (let j = 0;j < chars.length; j++) {
|
|
2482
|
+
const ch = chars[j];
|
|
2483
|
+
const canonical = aliasOf[ch] || ch;
|
|
2484
|
+
if (j === chars.length - 1 && !booleans.has(canonical) && !booleans.has(ch)) {
|
|
2485
|
+
const next = argv[i + 1];
|
|
2486
|
+
if (next !== undefined && !next.startsWith("-")) {
|
|
2487
|
+
setKey(ch, next);
|
|
2488
|
+
i++;
|
|
2489
|
+
} else {
|
|
2490
|
+
setKey(ch, true);
|
|
2491
|
+
}
|
|
2492
|
+
} else {
|
|
2493
|
+
setKey(ch, true);
|
|
2494
|
+
}
|
|
2495
|
+
}
|
|
2496
|
+
} else {
|
|
2497
|
+
result._.push(arg);
|
|
2498
|
+
}
|
|
2499
|
+
}
|
|
2500
|
+
return result;
|
|
2501
|
+
}
|
|
2407
2502
|
|
|
2408
2503
|
class Option {
|
|
2409
2504
|
rawName;
|
|
@@ -2551,9 +2646,9 @@ class Command {
|
|
|
2551
2646
|
return this.namespace ? `${this.namespace}:${this.name}` : this.name;
|
|
2552
2647
|
}
|
|
2553
2648
|
hasOption(name) {
|
|
2554
|
-
|
|
2649
|
+
const normalized = camelcaseOptionName(name.split(".")[0]);
|
|
2555
2650
|
return !!this.options.find((option) => {
|
|
2556
|
-
return option.names.
|
|
2651
|
+
return option.names.some((optionName) => camelcaseOptionName(optionName) === normalized);
|
|
2557
2652
|
});
|
|
2558
2653
|
}
|
|
2559
2654
|
outputHelp() {
|
|
@@ -2588,6 +2683,7 @@ class Command {
|
|
|
2588
2683
|
}
|
|
2589
2684
|
}
|
|
2590
2685
|
let commandBody = "";
|
|
2686
|
+
noNamespaceCommands.sort((a, b) => a.rawName < b.rawName ? -1 : a.rawName > b.rawName ? 1 : 0);
|
|
2591
2687
|
if (noNamespaceCommands.length > 0) {
|
|
2592
2688
|
commandBody += noNamespaceCommands.map((command) => {
|
|
2593
2689
|
return ` ${padRight(command.rawName, longestCommandName.length)} ${command.description}`;
|
|
@@ -2614,12 +2710,14 @@ class Command {
|
|
|
2614
2710
|
body: commandBody
|
|
2615
2711
|
});
|
|
2616
2712
|
sections.push({
|
|
2617
|
-
|
|
2618
|
-
body: commands.map((command) => ` $ ${name}${command.displayName === "" ? "" : ` ${command.displayName}`} --help`).join(`
|
|
2619
|
-
`)
|
|
2713
|
+
body: `Run \`${name} <command> --help\` for command details.`
|
|
2620
2714
|
});
|
|
2621
2715
|
}
|
|
2622
|
-
|
|
2716
|
+
const localOptionNames = new Set(this.options.flatMap((option) => option.names));
|
|
2717
|
+
let options = this.isGlobalCommand ? globalOptions : [
|
|
2718
|
+
...this.options,
|
|
2719
|
+
...(globalOptions || []).filter((option) => !option.names.some((name2) => localOptionNames.has(name2)))
|
|
2720
|
+
];
|
|
2623
2721
|
if (!this.isGlobalCommand && !this.isDefaultCommand) {
|
|
2624
2722
|
options = options.filter((option) => option.name !== "version");
|
|
2625
2723
|
}
|
|
@@ -2663,6 +2761,9 @@ ${section.body}` : section.body;
|
|
|
2663
2761
|
console.log(`${name}/${versionNumber} ${platformInfo3}`);
|
|
2664
2762
|
}
|
|
2665
2763
|
}
|
|
2764
|
+
get usageLine() {
|
|
2765
|
+
return `$ ${this.cli.name} ${this.usageText || this.rawName}`;
|
|
2766
|
+
}
|
|
2666
2767
|
checkRequiredArgs() {
|
|
2667
2768
|
const minimalArgsCount = this.args.filter((arg) => arg.required).length;
|
|
2668
2769
|
if (this.cli.args.length < minimalArgsCount) {
|
|
@@ -2671,7 +2772,7 @@ ${section.body}` : section.body;
|
|
|
2671
2772
|
const argNames = missingArgs.map((arg) => `<${arg.value}>`).join(" ");
|
|
2672
2773
|
throw new ClappError(`Missing required argument${missingArgs.length > 1 ? "s" : ""}: ${argNames}
|
|
2673
2774
|
|
|
2674
|
-
` + `Run \`${this.cli.name} ${this.rawName} --help\` for usage information
|
|
2775
|
+
` + `Run \`${this.cli.name} ${this.rawName} --help\` for usage information.`, this.usageLine);
|
|
2675
2776
|
}
|
|
2676
2777
|
}
|
|
2677
2778
|
checkUnknownOptions() {
|
|
@@ -2681,8 +2782,9 @@ ${section.body}` : section.body;
|
|
|
2681
2782
|
if (name !== "--" && !this.hasOption(name) && !globalCommand.hasOption(name)) {
|
|
2682
2783
|
const allOptions = [...globalCommand.options, ...this.options];
|
|
2683
2784
|
const allOptionNames = allOptions.flatMap((opt) => opt.names);
|
|
2785
|
+
const normalizedName = camelcaseOptionName(name.split(".")[0]);
|
|
2684
2786
|
const optionFlag = name.length > 1 ? `--${name}` : `-${name}`;
|
|
2685
|
-
const suggestions = findSimilarCommands(
|
|
2787
|
+
const suggestions = findSimilarCommands(normalizedName, allOptionNames);
|
|
2686
2788
|
let errorMsg = `Unknown option \`${optionFlag}\``;
|
|
2687
2789
|
if (suggestions.length > 0) {
|
|
2688
2790
|
errorMsg += `
|
|
@@ -2697,7 +2799,7 @@ Did you mean one of these?`;
|
|
|
2697
2799
|
errorMsg += `
|
|
2698
2800
|
|
|
2699
2801
|
Run \`${this.cli.name} ${this.rawName} --help\` to see available options.`;
|
|
2700
|
-
throw new ClappError(errorMsg);
|
|
2802
|
+
throw new ClappError(errorMsg, this.usageLine);
|
|
2701
2803
|
}
|
|
2702
2804
|
}
|
|
2703
2805
|
}
|
|
@@ -2712,7 +2814,7 @@ Run \`${this.cli.name} ${this.rawName} --help\` to see available options.`;
|
|
|
2712
2814
|
if (value === true || value === false && !hasNegated) {
|
|
2713
2815
|
throw new ClappError(`Option \`${option.rawName}\` requires a value.
|
|
2714
2816
|
|
|
2715
|
-
` + `Example: ${this.cli.name} ${this.rawName} ${option.rawName} <value
|
|
2817
|
+
` + `Example: ${this.cli.name} ${this.rawName} ${option.rawName} <value>`, this.usageLine);
|
|
2716
2818
|
}
|
|
2717
2819
|
}
|
|
2718
2820
|
}
|
|
@@ -3018,6 +3120,25 @@ Received ${signal}, cleaning up...`);
|
|
|
3018
3120
|
break;
|
|
3019
3121
|
}
|
|
3020
3122
|
}
|
|
3123
|
+
if (shouldParse) {
|
|
3124
|
+
const nextToken = slicedArgv[1];
|
|
3125
|
+
if (nextToken && !nextToken.startsWith("-")) {
|
|
3126
|
+
const joinedName = `${candidateName}:${nextToken}`;
|
|
3127
|
+
for (const command of this.commands) {
|
|
3128
|
+
if (command.isMatched(joinedName)) {
|
|
3129
|
+
const parsed = this.mri(slicedArgv, command);
|
|
3130
|
+
shouldParse = false;
|
|
3131
|
+
const parsedInfo = {
|
|
3132
|
+
...parsed,
|
|
3133
|
+
args: parsed.args.slice(2)
|
|
3134
|
+
};
|
|
3135
|
+
this.setParsedInfo(parsedInfo, command, joinedName);
|
|
3136
|
+
this.emit(`command:${joinedName}`, command);
|
|
3137
|
+
break;
|
|
3138
|
+
}
|
|
3139
|
+
}
|
|
3140
|
+
}
|
|
3141
|
+
}
|
|
3021
3142
|
}
|
|
3022
3143
|
if (shouldParse) {
|
|
3023
3144
|
for (const command of this.commands) {
|
|
@@ -3043,7 +3164,7 @@ Received ${signal}, cleaning up...`);
|
|
|
3043
3164
|
if (this.options.debug) {
|
|
3044
3165
|
this.isDebug = true;
|
|
3045
3166
|
}
|
|
3046
|
-
if (this.options.noInteraction) {
|
|
3167
|
+
if (this.options.noInteraction || this.options.interaction === false) {
|
|
3047
3168
|
this.isNoInteraction = true;
|
|
3048
3169
|
}
|
|
3049
3170
|
if (this.options.env) {
|
|
@@ -3055,14 +3176,14 @@ Received ${signal}, cleaning up...`);
|
|
|
3055
3176
|
if (this.options.force) {
|
|
3056
3177
|
this.isForce = true;
|
|
3057
3178
|
}
|
|
3058
|
-
if (this.options.noEmoji
|
|
3059
|
-
this.useEmoji =
|
|
3179
|
+
if (this.options.noEmoji || this.options.emoji === false) {
|
|
3180
|
+
this.useEmoji = false;
|
|
3060
3181
|
}
|
|
3061
3182
|
if (this.options.theme) {
|
|
3062
3183
|
this.theme = String(this.options.theme);
|
|
3063
3184
|
}
|
|
3064
|
-
if (this.options.noCache
|
|
3065
|
-
this.isNoCache =
|
|
3185
|
+
if (this.options.noCache || this.options.cache === false) {
|
|
3186
|
+
this.isNoCache = true;
|
|
3066
3187
|
}
|
|
3067
3188
|
if (this.options.help && this.showHelpOnExit) {
|
|
3068
3189
|
this.outputHelp();
|
|
@@ -3143,18 +3264,16 @@ Received ${signal}, cleaning up...`);
|
|
|
3143
3264
|
return this.parse(argv, { run: true, exitOnError: true });
|
|
3144
3265
|
}
|
|
3145
3266
|
handleUsageError(err) {
|
|
3146
|
-
|
|
3147
|
-
const isUsage = isClappError && err.isUsageError !== false;
|
|
3148
|
-
if (!isUsage)
|
|
3267
|
+
if (!isClappError(err) || err.isUsageError === false)
|
|
3149
3268
|
return;
|
|
3150
|
-
const
|
|
3151
|
-
const raw = e.message ?? "command-line error";
|
|
3269
|
+
const raw = err.message || "command-line error";
|
|
3152
3270
|
const label = this.name ? `${this.name}: ` : "";
|
|
3153
|
-
const suffix = /--help/.test(raw) ? "" : `
|
|
3271
|
+
const suffix = /--help/.test(raw) ? "" : err.usage ? `
|
|
3272
|
+
Usage: ${err.usage}` : `
|
|
3154
3273
|
Run \`${this.name ?? "cli"} --help\` for usage.`;
|
|
3155
3274
|
process5.stderr.write(`${label}${raw}${suffix}
|
|
3156
3275
|
`);
|
|
3157
|
-
process5.exit(
|
|
3276
|
+
process5.exit(err.exitCode ?? 2);
|
|
3158
3277
|
}
|
|
3159
3278
|
async runMatchedCommand() {
|
|
3160
3279
|
const { args, options, matchedCommand: command } = this;
|
|
@@ -4072,7 +4191,7 @@ function craftBinaryNotFoundMessage(triedPath) {
|
|
|
4072
4191
|
`);
|
|
4073
4192
|
}
|
|
4074
4193
|
// package.json
|
|
4075
|
-
var version = "0.0.
|
|
4194
|
+
var version = "0.0.77";
|
|
4076
4195
|
|
|
4077
4196
|
// bin/cli.ts
|
|
4078
4197
|
var spawnedFrom = process6.env[CRAFT_CLI_SPAWN_MARKER];
|
package/dist/index.cjs
CHANGED
|
@@ -425,6 +425,7 @@ async function packageMacOS(config, outDir) {
|
|
|
425
425
|
category: opts.category,
|
|
426
426
|
minimumSystemVersion: opts.minimumSystemVersion,
|
|
427
427
|
menuBarOnly: opts.menuBarOnly,
|
|
428
|
+
urlSchemes: opts.urlSchemes,
|
|
428
429
|
binaryPath: config.binaryPath,
|
|
429
430
|
iconPath: config.iconPath,
|
|
430
431
|
provisioningProfile: opts.provisioningProfile,
|
|
@@ -621,6 +622,37 @@ async function packageLinux(config, outDir) {
|
|
|
621
622
|
}
|
|
622
623
|
return results;
|
|
623
624
|
}
|
|
625
|
+
var URL_SCHEME = /^[a-z][a-z0-9+.-]*$/i;
|
|
626
|
+
function urlTypesEntry(bundleId, schemes) {
|
|
627
|
+
const seen = new Set;
|
|
628
|
+
const unique = [];
|
|
629
|
+
for (const scheme of schemes) {
|
|
630
|
+
if (!URL_SCHEME.test(scheme))
|
|
631
|
+
throw new Error(`Invalid URL scheme ${JSON.stringify(scheme)}: must start with a letter and contain only letters, digits, "+", "-" or "."`);
|
|
632
|
+
const key = scheme.toLowerCase();
|
|
633
|
+
if (seen.has(key))
|
|
634
|
+
continue;
|
|
635
|
+
seen.add(key);
|
|
636
|
+
unique.push(scheme);
|
|
637
|
+
}
|
|
638
|
+
if (unique.length === 0)
|
|
639
|
+
return null;
|
|
640
|
+
const list = unique.map((scheme) => ` <string>${xml(scheme)}</string>`).join(`
|
|
641
|
+
`);
|
|
642
|
+
return ` <key>CFBundleURLTypes</key>
|
|
643
|
+
<array>
|
|
644
|
+
<dict>
|
|
645
|
+
<key>CFBundleURLName</key>
|
|
646
|
+
<string>${xml(bundleId)}</string>
|
|
647
|
+
<key>CFBundleTypeRole</key>
|
|
648
|
+
<string>Viewer</string>
|
|
649
|
+
<key>CFBundleURLSchemes</key>
|
|
650
|
+
<array>
|
|
651
|
+
${list}
|
|
652
|
+
</array>
|
|
653
|
+
</dict>
|
|
654
|
+
</array>`;
|
|
655
|
+
}
|
|
624
656
|
function macOSInfoPlist(metadata) {
|
|
625
657
|
const entries = [
|
|
626
658
|
["CFBundleExecutable", metadata.name],
|
|
@@ -639,9 +671,12 @@ function macOSInfoPlist(metadata) {
|
|
|
639
671
|
entries.push(["LSMinimumSystemVersion", metadata.minimumSystemVersion]);
|
|
640
672
|
if (metadata.menuBarOnly)
|
|
641
673
|
entries.push(["LSUIElement", true]);
|
|
642
|
-
const
|
|
674
|
+
const scalars = entries.map(([key, value]) => ` <key>${xml(key)}</key>
|
|
643
675
|
${value === true ? " <true/>" : ` <string>${xml(value)}</string>`}`).join(`
|
|
644
676
|
`);
|
|
677
|
+
const urlTypes = urlTypesEntry(metadata.bundleId, metadata.urlSchemes || []);
|
|
678
|
+
const body = urlTypes ? `${scalars}
|
|
679
|
+
${urlTypes}` : scalars;
|
|
645
680
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
646
681
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
647
682
|
<plist version="1.0">
|
|
@@ -794,6 +829,43 @@ async function createDMG(opts) {
|
|
|
794
829
|
return { success: false, error: failures.join(`
|
|
795
830
|
`) };
|
|
796
831
|
}
|
|
832
|
+
function pkgbuildComponentPlist(rootRelativeBundlePath) {
|
|
833
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
834
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
835
|
+
<plist version="1.0">
|
|
836
|
+
<array>
|
|
837
|
+
<dict>
|
|
838
|
+
<key>BundleHasStrictIdentifier</key>
|
|
839
|
+
<true/>
|
|
840
|
+
<key>BundleIsRelocatable</key>
|
|
841
|
+
<false/>
|
|
842
|
+
<key>BundleIsVersionChecked</key>
|
|
843
|
+
<false/>
|
|
844
|
+
<key>BundleOverwriteAction</key>
|
|
845
|
+
<string>upgrade</string>
|
|
846
|
+
<key>RootRelativeBundlePath</key>
|
|
847
|
+
<string>${xml(rootRelativeBundlePath)}</string>
|
|
848
|
+
</dict>
|
|
849
|
+
</array>
|
|
850
|
+
</plist>
|
|
851
|
+
`;
|
|
852
|
+
}
|
|
853
|
+
function pkgbuildArguments(opts) {
|
|
854
|
+
return [
|
|
855
|
+
"--root",
|
|
856
|
+
opts.root,
|
|
857
|
+
"--component-plist",
|
|
858
|
+
opts.componentPlistPath,
|
|
859
|
+
"--identifier",
|
|
860
|
+
opts.identifier,
|
|
861
|
+
"--version",
|
|
862
|
+
opts.version,
|
|
863
|
+
"--install-location",
|
|
864
|
+
"/",
|
|
865
|
+
...opts.installerIdentity ? ["--sign", opts.installerIdentity] : [],
|
|
866
|
+
opts.outputPath
|
|
867
|
+
];
|
|
868
|
+
}
|
|
797
869
|
async function createPKG(opts) {
|
|
798
870
|
if (!/^[a-zA-Z0-9._-]+$/.test(opts.identifier) || !opts.identifier.includes("."))
|
|
799
871
|
return { success: false, error: `Invalid pkg identifier "${opts.identifier}"; expected reverse-DNS like com.example.app` };
|
|
@@ -811,21 +883,22 @@ async function createPKG(opts) {
|
|
|
811
883
|
}
|
|
812
884
|
const tempDir = import_fs.mkdtempSync(import_path.join(import_os.tmpdir(), "craft-pkg-"));
|
|
813
885
|
try {
|
|
814
|
-
const
|
|
815
|
-
|
|
816
|
-
import_fs.
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
opts.outputPath
|
|
828
|
-
|
|
886
|
+
const root = import_path.join(tempDir, "root");
|
|
887
|
+
const appsDir = import_path.join(root, "Applications");
|
|
888
|
+
import_fs.mkdirSync(appsDir, { recursive: true, mode: 493 });
|
|
889
|
+
import_fs.chmodSync(root, 493);
|
|
890
|
+
const bundleName = import_path.basename(opts.appBundlePath);
|
|
891
|
+
import_fs.cpSync(opts.appBundlePath, import_path.join(appsDir, bundleName), { recursive: true });
|
|
892
|
+
const componentPlistPath = import_path.join(tempDir, "component.plist");
|
|
893
|
+
import_fs.writeFileSync(componentPlistPath, pkgbuildComponentPlist(`Applications/${bundleName}`));
|
|
894
|
+
const built = await runTool("pkgbuild", pkgbuildArguments({
|
|
895
|
+
root,
|
|
896
|
+
componentPlistPath,
|
|
897
|
+
identifier: opts.identifier,
|
|
898
|
+
version: opts.version,
|
|
899
|
+
outputPath: opts.outputPath,
|
|
900
|
+
installerIdentity: opts.installerIdentity
|
|
901
|
+
}));
|
|
829
902
|
return built.success ? { success: true, outputPath: opts.outputPath } : { success: false, error: built.error };
|
|
830
903
|
} finally {
|
|
831
904
|
import_fs.rmSync(tempDir, { recursive: true, force: true });
|
|
@@ -10624,7 +10697,9 @@ class CraftApp {
|
|
|
10624
10697
|
}
|
|
10625
10698
|
buildArgs() {
|
|
10626
10699
|
const args = [];
|
|
10627
|
-
const { window: window3, html, url } = this.config;
|
|
10700
|
+
const { window: window3, html, url, appName } = this.config;
|
|
10701
|
+
if (appName)
|
|
10702
|
+
args.push("--app-name", appName);
|
|
10628
10703
|
if (!window3?.menubarOnly) {
|
|
10629
10704
|
if (url) {
|
|
10630
10705
|
args.push("--url", url);
|
|
@@ -10658,6 +10733,8 @@ class CraftApp {
|
|
|
10658
10733
|
args.push("--hot-reload");
|
|
10659
10734
|
if (window3?.devTools === false)
|
|
10660
10735
|
args.push("--no-devtools");
|
|
10736
|
+
else if (window3?.devTools === true)
|
|
10737
|
+
args.push("--dev-tools");
|
|
10661
10738
|
if (window3?.systemTray)
|
|
10662
10739
|
args.push("--system-tray");
|
|
10663
10740
|
if (window3?.hideDockIcon)
|
|
@@ -10666,6 +10743,8 @@ class CraftApp {
|
|
|
10666
10743
|
args.push("--menubar-only");
|
|
10667
10744
|
if (window3?.titlebarHidden)
|
|
10668
10745
|
args.push("--titlebar-hidden");
|
|
10746
|
+
if (window3?.headless)
|
|
10747
|
+
args.push("--headless");
|
|
10669
10748
|
if (window3?.webSidebarMaterial) {
|
|
10670
10749
|
args.push("--web-sidebar-material");
|
|
10671
10750
|
if (window3?.webSidebarWidth)
|
|
@@ -10675,6 +10754,8 @@ class CraftApp {
|
|
|
10675
10754
|
}
|
|
10676
10755
|
if (window3?.icon)
|
|
10677
10756
|
args.push("--icon", window3.icon);
|
|
10757
|
+
if (window3?.frameAutosave)
|
|
10758
|
+
args.push("--frame-autosave", window3.frameAutosave);
|
|
10678
10759
|
if (window3?.nativeSidebar) {
|
|
10679
10760
|
args.push("--native-sidebar");
|
|
10680
10761
|
if (window3?.sidebarWidth)
|
package/dist/index.d.cts
CHANGED
|
@@ -59,4 +59,4 @@ export declare function show(html: string, options?: WindowOptions): Promise<voi
|
|
|
59
59
|
* Quick helper to load a URL
|
|
60
60
|
*/
|
|
61
61
|
export declare function loadURL(url: string, options?: WindowOptions): Promise<void>;
|
|
62
|
-
export type { WindowOptions, CraftSidebarAPI, AppConfig, CraftTrayAPI, CraftWindowAPI, CraftAppAPI, CraftBridgeAPI, Permission, HapticType, CameraOptions, PhotoPickerOptions, CraftMobileAPI, CraftFileSystemAPI, CraftDatabaseAPI, CraftHttpAPI, CraftCryptoAPI, CraftEventType, CraftEventMap, CraftEventHandler, CraftEventEmitter, IOSConfig, AndroidConfig, MacOSConfig, WindowsConfig, LinuxConfig, CraftAppConfig, } from './types.js';
|
|
62
|
+
export type { WindowOptions, CraftSidebarAPI, AppConfig, CraftTrayAPI, CraftWindowAPI, CraftAppAPI, CraftBridgeAPI, Permission, HapticType, CameraOptions, PhotoPickerOptions, CraftMobileAPI, CraftFileSystemAPI, 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
|
@@ -59,4 +59,4 @@ export declare function show(html: string, options?: WindowOptions): Promise<voi
|
|
|
59
59
|
* Quick helper to load a URL
|
|
60
60
|
*/
|
|
61
61
|
export declare function loadURL(url: string, options?: WindowOptions): Promise<void>;
|
|
62
|
-
export type { WindowOptions, CraftSidebarAPI, AppConfig, CraftTrayAPI, CraftWindowAPI, CraftAppAPI, CraftBridgeAPI, Permission, HapticType, CameraOptions, PhotoPickerOptions, CraftMobileAPI, CraftFileSystemAPI, CraftDatabaseAPI, CraftHttpAPI, CraftCryptoAPI, CraftEventType, CraftEventMap, CraftEventHandler, CraftEventEmitter, IOSConfig, AndroidConfig, MacOSConfig, WindowsConfig, LinuxConfig, CraftAppConfig, } from './types.js';
|
|
62
|
+
export type { WindowOptions, CraftSidebarAPI, AppConfig, CraftTrayAPI, CraftWindowAPI, CraftAppAPI, CraftBridgeAPI, Permission, HapticType, CameraOptions, PhotoPickerOptions, CraftMobileAPI, CraftFileSystemAPI, 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.js
CHANGED
|
@@ -195,6 +195,7 @@ async function packageMacOS(config, outDir) {
|
|
|
195
195
|
category: opts.category,
|
|
196
196
|
minimumSystemVersion: opts.minimumSystemVersion,
|
|
197
197
|
menuBarOnly: opts.menuBarOnly,
|
|
198
|
+
urlSchemes: opts.urlSchemes,
|
|
198
199
|
binaryPath: config.binaryPath,
|
|
199
200
|
iconPath: config.iconPath,
|
|
200
201
|
provisioningProfile: opts.provisioningProfile,
|
|
@@ -391,6 +392,37 @@ async function packageLinux(config, outDir) {
|
|
|
391
392
|
}
|
|
392
393
|
return results;
|
|
393
394
|
}
|
|
395
|
+
var URL_SCHEME = /^[a-z][a-z0-9+.-]*$/i;
|
|
396
|
+
function urlTypesEntry(bundleId, schemes) {
|
|
397
|
+
const seen = new Set;
|
|
398
|
+
const unique = [];
|
|
399
|
+
for (const scheme of schemes) {
|
|
400
|
+
if (!URL_SCHEME.test(scheme))
|
|
401
|
+
throw new Error(`Invalid URL scheme ${JSON.stringify(scheme)}: must start with a letter and contain only letters, digits, "+", "-" or "."`);
|
|
402
|
+
const key = scheme.toLowerCase();
|
|
403
|
+
if (seen.has(key))
|
|
404
|
+
continue;
|
|
405
|
+
seen.add(key);
|
|
406
|
+
unique.push(scheme);
|
|
407
|
+
}
|
|
408
|
+
if (unique.length === 0)
|
|
409
|
+
return null;
|
|
410
|
+
const list = unique.map((scheme) => ` <string>${xml(scheme)}</string>`).join(`
|
|
411
|
+
`);
|
|
412
|
+
return ` <key>CFBundleURLTypes</key>
|
|
413
|
+
<array>
|
|
414
|
+
<dict>
|
|
415
|
+
<key>CFBundleURLName</key>
|
|
416
|
+
<string>${xml(bundleId)}</string>
|
|
417
|
+
<key>CFBundleTypeRole</key>
|
|
418
|
+
<string>Viewer</string>
|
|
419
|
+
<key>CFBundleURLSchemes</key>
|
|
420
|
+
<array>
|
|
421
|
+
${list}
|
|
422
|
+
</array>
|
|
423
|
+
</dict>
|
|
424
|
+
</array>`;
|
|
425
|
+
}
|
|
394
426
|
function macOSInfoPlist(metadata) {
|
|
395
427
|
const entries = [
|
|
396
428
|
["CFBundleExecutable", metadata.name],
|
|
@@ -409,9 +441,12 @@ function macOSInfoPlist(metadata) {
|
|
|
409
441
|
entries.push(["LSMinimumSystemVersion", metadata.minimumSystemVersion]);
|
|
410
442
|
if (metadata.menuBarOnly)
|
|
411
443
|
entries.push(["LSUIElement", true]);
|
|
412
|
-
const
|
|
444
|
+
const scalars = entries.map(([key, value]) => ` <key>${xml(key)}</key>
|
|
413
445
|
${value === true ? " <true/>" : ` <string>${xml(value)}</string>`}`).join(`
|
|
414
446
|
`);
|
|
447
|
+
const urlTypes = urlTypesEntry(metadata.bundleId, metadata.urlSchemes || []);
|
|
448
|
+
const body = urlTypes ? `${scalars}
|
|
449
|
+
${urlTypes}` : scalars;
|
|
415
450
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
416
451
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
417
452
|
<plist version="1.0">
|
|
@@ -564,6 +599,43 @@ async function createDMG(opts) {
|
|
|
564
599
|
return { success: false, error: failures.join(`
|
|
565
600
|
`) };
|
|
566
601
|
}
|
|
602
|
+
function pkgbuildComponentPlist(rootRelativeBundlePath) {
|
|
603
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
604
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
605
|
+
<plist version="1.0">
|
|
606
|
+
<array>
|
|
607
|
+
<dict>
|
|
608
|
+
<key>BundleHasStrictIdentifier</key>
|
|
609
|
+
<true/>
|
|
610
|
+
<key>BundleIsRelocatable</key>
|
|
611
|
+
<false/>
|
|
612
|
+
<key>BundleIsVersionChecked</key>
|
|
613
|
+
<false/>
|
|
614
|
+
<key>BundleOverwriteAction</key>
|
|
615
|
+
<string>upgrade</string>
|
|
616
|
+
<key>RootRelativeBundlePath</key>
|
|
617
|
+
<string>${xml(rootRelativeBundlePath)}</string>
|
|
618
|
+
</dict>
|
|
619
|
+
</array>
|
|
620
|
+
</plist>
|
|
621
|
+
`;
|
|
622
|
+
}
|
|
623
|
+
function pkgbuildArguments(opts) {
|
|
624
|
+
return [
|
|
625
|
+
"--root",
|
|
626
|
+
opts.root,
|
|
627
|
+
"--component-plist",
|
|
628
|
+
opts.componentPlistPath,
|
|
629
|
+
"--identifier",
|
|
630
|
+
opts.identifier,
|
|
631
|
+
"--version",
|
|
632
|
+
opts.version,
|
|
633
|
+
"--install-location",
|
|
634
|
+
"/",
|
|
635
|
+
...opts.installerIdentity ? ["--sign", opts.installerIdentity] : [],
|
|
636
|
+
opts.outputPath
|
|
637
|
+
];
|
|
638
|
+
}
|
|
567
639
|
async function createPKG(opts) {
|
|
568
640
|
if (!/^[a-zA-Z0-9._-]+$/.test(opts.identifier) || !opts.identifier.includes("."))
|
|
569
641
|
return { success: false, error: `Invalid pkg identifier "${opts.identifier}"; expected reverse-DNS like com.example.app` };
|
|
@@ -581,21 +653,22 @@ async function createPKG(opts) {
|
|
|
581
653
|
}
|
|
582
654
|
const tempDir = mkdtempSync(join(tmpdir(), "craft-pkg-"));
|
|
583
655
|
try {
|
|
584
|
-
const
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
opts.outputPath
|
|
598
|
-
|
|
656
|
+
const root = join(tempDir, "root");
|
|
657
|
+
const appsDir = join(root, "Applications");
|
|
658
|
+
mkdirSync(appsDir, { recursive: true, mode: 493 });
|
|
659
|
+
chmodSync(root, 493);
|
|
660
|
+
const bundleName = basename(opts.appBundlePath);
|
|
661
|
+
cpSync(opts.appBundlePath, join(appsDir, bundleName), { recursive: true });
|
|
662
|
+
const componentPlistPath = join(tempDir, "component.plist");
|
|
663
|
+
writeFileSync(componentPlistPath, pkgbuildComponentPlist(`Applications/${bundleName}`));
|
|
664
|
+
const built = await runTool("pkgbuild", pkgbuildArguments({
|
|
665
|
+
root,
|
|
666
|
+
componentPlistPath,
|
|
667
|
+
identifier: opts.identifier,
|
|
668
|
+
version: opts.version,
|
|
669
|
+
outputPath: opts.outputPath,
|
|
670
|
+
installerIdentity: opts.installerIdentity
|
|
671
|
+
}));
|
|
599
672
|
return built.success ? { success: true, outputPath: opts.outputPath } : { success: false, error: built.error };
|
|
600
673
|
} finally {
|
|
601
674
|
rmSync(tempDir, { recursive: true, force: true });
|
|
@@ -10394,7 +10467,9 @@ class CraftApp {
|
|
|
10394
10467
|
}
|
|
10395
10468
|
buildArgs() {
|
|
10396
10469
|
const args = [];
|
|
10397
|
-
const { window: window3, html, url } = this.config;
|
|
10470
|
+
const { window: window3, html, url, appName } = this.config;
|
|
10471
|
+
if (appName)
|
|
10472
|
+
args.push("--app-name", appName);
|
|
10398
10473
|
if (!window3?.menubarOnly) {
|
|
10399
10474
|
if (url) {
|
|
10400
10475
|
args.push("--url", url);
|
|
@@ -10428,6 +10503,8 @@ class CraftApp {
|
|
|
10428
10503
|
args.push("--hot-reload");
|
|
10429
10504
|
if (window3?.devTools === false)
|
|
10430
10505
|
args.push("--no-devtools");
|
|
10506
|
+
else if (window3?.devTools === true)
|
|
10507
|
+
args.push("--dev-tools");
|
|
10431
10508
|
if (window3?.systemTray)
|
|
10432
10509
|
args.push("--system-tray");
|
|
10433
10510
|
if (window3?.hideDockIcon)
|
|
@@ -10436,6 +10513,8 @@ class CraftApp {
|
|
|
10436
10513
|
args.push("--menubar-only");
|
|
10437
10514
|
if (window3?.titlebarHidden)
|
|
10438
10515
|
args.push("--titlebar-hidden");
|
|
10516
|
+
if (window3?.headless)
|
|
10517
|
+
args.push("--headless");
|
|
10439
10518
|
if (window3?.webSidebarMaterial) {
|
|
10440
10519
|
args.push("--web-sidebar-material");
|
|
10441
10520
|
if (window3?.webSidebarWidth)
|
|
@@ -10445,6 +10524,8 @@ class CraftApp {
|
|
|
10445
10524
|
}
|
|
10446
10525
|
if (window3?.icon)
|
|
10447
10526
|
args.push("--icon", window3.icon);
|
|
10527
|
+
if (window3?.frameAutosave)
|
|
10528
|
+
args.push("--frame-autosave", window3.frameAutosave);
|
|
10448
10529
|
if (window3?.nativeSidebar) {
|
|
10449
10530
|
args.push("--native-sidebar");
|
|
10450
10531
|
if (window3?.sidebarWidth)
|
package/dist/package.d.ts
CHANGED
|
@@ -74,6 +74,20 @@ export interface PackageConfig {
|
|
|
74
74
|
* the helper lands next to `process.execPath`.
|
|
75
75
|
*/
|
|
76
76
|
additionalExecutables?: string[];
|
|
77
|
+
/**
|
|
78
|
+
* URL schemes the app registers as a handler for — `['myapp']` makes
|
|
79
|
+
* `myapp://…` open it.
|
|
80
|
+
*
|
|
81
|
+
* Emitted as `CFBundleURLTypes`. Without this key macOS never dispatches
|
|
82
|
+
* a URL to the app at all, so Craft's receive path (the `kAEGetURL`
|
|
83
|
+
* AppleEvent handler behind `craft.deepLink`) can be fully working and
|
|
84
|
+
* still never hear anything.
|
|
85
|
+
*
|
|
86
|
+
* Schemes are matched case-insensitively by LaunchServices and are
|
|
87
|
+
* global to the machine: pick something specific to the app, not `app`
|
|
88
|
+
* or `open`.
|
|
89
|
+
*/
|
|
90
|
+
urlSchemes?: string[];
|
|
77
91
|
};
|
|
78
92
|
/** Windows-specific options */
|
|
79
93
|
windows?: {
|
|
@@ -138,7 +152,18 @@ export interface MacOSBundleMetadata {
|
|
|
138
152
|
minimumSystemVersion?: string;
|
|
139
153
|
/** Sets `LSUIElement` — no Dock icon, no app switcher entry */
|
|
140
154
|
menuBarOnly?: boolean;
|
|
155
|
+
/** URL schemes the app handles, emitted as `CFBundleURLTypes` */
|
|
156
|
+
urlSchemes?: string[];
|
|
141
157
|
}
|
|
158
|
+
/**
|
|
159
|
+
* `CFBundleURLTypes` for the schemes an app handles, or `null` if it handles
|
|
160
|
+
* none.
|
|
161
|
+
*
|
|
162
|
+
* All the schemes go in one URL type. They are alternatives for reaching the
|
|
163
|
+
* same app, not different kinds of document, so splitting them across several
|
|
164
|
+
* dicts would only invent distinctions LaunchServices does not care about.
|
|
165
|
+
*/
|
|
166
|
+
export declare function urlTypesEntry(bundleId: string, schemes: readonly string[]): string | null;
|
|
142
167
|
/**
|
|
143
168
|
* Render `Contents/Info.plist` for a macOS app bundle.
|
|
144
169
|
*
|
|
@@ -178,6 +203,50 @@ export declare function dmgCreateArguments(opts: {
|
|
|
178
203
|
volumeName: string;
|
|
179
204
|
}, contentBytes: number): string[];
|
|
180
205
|
export declare function shouldRetryHdiutil(error: string, attempt: number, maxAttempts?: number): boolean;
|
|
206
|
+
/**
|
|
207
|
+
* Helper: Create PKG from app bundle
|
|
208
|
+
*
|
|
209
|
+
* Two flavours share this entry point:
|
|
210
|
+
* - a plain `pkgbuild` installer for direct distribution, and
|
|
211
|
+
* - a signed `productbuild` submission package for the Mac App Store, which is
|
|
212
|
+
* the only form App Store Connect accepts.
|
|
213
|
+
*/
|
|
214
|
+
/**
|
|
215
|
+
* The component property list `pkgbuild` is given for the app bundle.
|
|
216
|
+
*
|
|
217
|
+
* Written out rather than left to `pkgbuild`'s inference, because its default
|
|
218
|
+
* for a bundle is `BundleIsRelocatable = true`, which puts this in the package:
|
|
219
|
+
*
|
|
220
|
+
* <relocate><bundle id="dev.example.app"/></relocate>
|
|
221
|
+
*
|
|
222
|
+
* That directive tells `installer` the payload path is only a suggestion: it
|
|
223
|
+
* looks the bundle identifier up on the target volume and, if the system has a
|
|
224
|
+
* copy of that bundle registered anywhere, writes the payload over *that* copy
|
|
225
|
+
* instead — and exits 0. The user sees "The install was successful" and finds
|
|
226
|
+
* nothing at /Applications.
|
|
227
|
+
*
|
|
228
|
+
* It is intermittent by nature, since it turns on whether the system has
|
|
229
|
+
* indexed some other copy yet, which is exactly how it behaved: the native
|
|
230
|
+
* lifecycle workflow's macOS legs failed only on slow runs, on both
|
|
231
|
+
* architectures, with `installer` reporting success and the app absent.
|
|
232
|
+
*
|
|
233
|
+
* `BundleIsVersionChecked` is off for a related reason — with it on, installing
|
|
234
|
+
* an *older* version over a newer one is skipped, which silently turns a
|
|
235
|
+
* rollback into a no-op.
|
|
236
|
+
*
|
|
237
|
+
* The `pkg-info` attribute `relocatable="false"` that appears either way is not
|
|
238
|
+
* this setting and does not govern it; only the `<relocate>` element does.
|
|
239
|
+
*/
|
|
240
|
+
export declare function pkgbuildComponentPlist(rootRelativeBundlePath: string): string;
|
|
241
|
+
/** Build the `pkgbuild` argument list for a non-App-Store package. */
|
|
242
|
+
export declare function pkgbuildArguments(opts: {
|
|
243
|
+
root: string;
|
|
244
|
+
componentPlistPath: string;
|
|
245
|
+
identifier: string;
|
|
246
|
+
version: string;
|
|
247
|
+
outputPath: string;
|
|
248
|
+
installerIdentity?: string;
|
|
249
|
+
}): string[];
|
|
181
250
|
export declare function windowsArchitecture(architecture: string): 'x86' | 'x64' | 'arm64';
|
|
182
251
|
export declare function renderWixSource(opts: Pick<MSIOptions, 'name' | 'version' | 'manufacturer' | 'architecture'>, sourceName: string): string;
|
|
183
252
|
export declare function windowsExecutableName(name: string): string;
|
package/dist/types.d.ts
CHANGED
|
@@ -55,6 +55,39 @@ export interface WindowOptions {
|
|
|
55
55
|
* Window title
|
|
56
56
|
*/
|
|
57
57
|
title?: string;
|
|
58
|
+
/**
|
|
59
|
+
* Build the window without ever putting it on screen (macOS).
|
|
60
|
+
*
|
|
61
|
+
* The page loads and runs JavaScript exactly as it would visibly, and it
|
|
62
|
+
* remains capturable — a snapshot taken after script has mutated the DOM
|
|
63
|
+
* reflects the mutation.
|
|
64
|
+
*
|
|
65
|
+
* **It does not animate.** An unshown window does not drive the compositor,
|
|
66
|
+
* so `requestAnimationFrame` stops after one frame and page timers fall to
|
|
67
|
+
* roughly 1Hz. Anything that advances itself — CSS or canvas animation, a
|
|
68
|
+
* charting library redrawing on rAF, "wait until the spinner stops" — sees
|
|
69
|
+
* the first frame forever. Drive the page with explicit calls and take
|
|
70
|
+
* readiness from load completion, not from a polling loop inside the page.
|
|
71
|
+
*
|
|
72
|
+
* Contradicts `menubarOnly`, which has no window to hide; passing both is an
|
|
73
|
+
* error rather than a silent no-op.
|
|
74
|
+
*/
|
|
75
|
+
headless?: boolean;
|
|
76
|
+
/**
|
|
77
|
+
* Remember this window's size and position across launches, under this name
|
|
78
|
+
* (macOS).
|
|
79
|
+
*
|
|
80
|
+
* `width`/`height`/`x`/`y` become **first-launch defaults**: they are what
|
|
81
|
+
* the window opens at until there is a saved frame to restore, and the saved
|
|
82
|
+
* frame wins from then on. Without this the window forgets its geometry
|
|
83
|
+
* every launch, and an app that wants the standard behaviour has to wire
|
|
84
|
+
* `onResize`/`onMove` to its own storage and reimplement what AppKit does in
|
|
85
|
+
* one call.
|
|
86
|
+
*
|
|
87
|
+
* The name is the key AppKit stores under, so it must be stable across
|
|
88
|
+
* launches and distinct per window.
|
|
89
|
+
*/
|
|
90
|
+
frameAutosave?: string;
|
|
58
91
|
/**
|
|
59
92
|
* Window width in pixels
|
|
60
93
|
* @default 800
|
|
@@ -454,6 +487,19 @@ export interface AppConfig {
|
|
|
454
487
|
* Window options
|
|
455
488
|
*/
|
|
456
489
|
window?: WindowOptions;
|
|
490
|
+
/**
|
|
491
|
+
* The name macOS shows for the app: the App menu title, and the
|
|
492
|
+
* "About X" / "Hide X" / "Quit X" items.
|
|
493
|
+
*
|
|
494
|
+
* Without it those read the executable's name, so every app launched
|
|
495
|
+
* through the shared `craft` binary calls itself "craft" in the menu bar.
|
|
496
|
+
* A packaged `.app` gets this from `CFBundleName` instead; this is what
|
|
497
|
+
* gives a dev-mode app its identity back without a packaging step.
|
|
498
|
+
*
|
|
499
|
+
* Not the name `ps` and Activity Monitor show — that comes from the
|
|
500
|
+
* executable and cannot be changed from inside the process.
|
|
501
|
+
*/
|
|
502
|
+
appName?: string;
|
|
457
503
|
/**
|
|
458
504
|
* Path to Craft binary (auto-detected if not provided)
|
|
459
505
|
*/
|
|
@@ -923,6 +969,254 @@ export interface CraftBridgeAPI {
|
|
|
923
969
|
* Screen-sharing and screen-recording detection (macOS)
|
|
924
970
|
*/
|
|
925
971
|
screenSharing?: CraftScreenSharingAPI;
|
|
972
|
+
/**
|
|
973
|
+
* System-wide hotkeys (macOS).
|
|
974
|
+
*
|
|
975
|
+
* Absent in effect on Linux and Windows: the calls exist, and every
|
|
976
|
+
* registration is refused, because Craft has no implementation there and a
|
|
977
|
+
* shortcut that can never fire is worse than one that was never accepted.
|
|
978
|
+
*/
|
|
979
|
+
shortcuts?: CraftGlobalShortcutsAPI;
|
|
980
|
+
/**
|
|
981
|
+
* A small scalar preference store (macOS: CFPreferences).
|
|
982
|
+
*/
|
|
983
|
+
prefs?: CraftPreferencesAPI;
|
|
984
|
+
/**
|
|
985
|
+
* The Cmd+, convention: where the App menu's Settings… item arrives.
|
|
986
|
+
*/
|
|
987
|
+
settings?: CraftSettingsAPI;
|
|
988
|
+
/**
|
|
989
|
+
* What the native side actually serves.
|
|
990
|
+
*
|
|
991
|
+
* Always present: it is how you find out whether anything else here is.
|
|
992
|
+
*/
|
|
993
|
+
capabilities?: () => Promise<CraftCapabilities>;
|
|
994
|
+
/** The last capabilities answer, or null if nothing has asked yet. */
|
|
995
|
+
capabilitiesSync?: () => CraftCapabilities | null;
|
|
996
|
+
/**
|
|
997
|
+
* Whether one surface is known to work — `craft.supports('tray.destroy')`.
|
|
998
|
+
*
|
|
999
|
+
* Fails **open**: returns true before capabilities have been fetched, and
|
|
1000
|
+
* true for a namespace craft has not audited. A feature-detection mechanism
|
|
1001
|
+
* that breaks working code when it cannot see itself would be worse than the
|
|
1002
|
+
* gaps it describes.
|
|
1003
|
+
*/
|
|
1004
|
+
supports?: (path: string) => boolean;
|
|
1005
|
+
}
|
|
1006
|
+
/**
|
|
1007
|
+
* How much craft is willing to claim about a namespace.
|
|
1008
|
+
*
|
|
1009
|
+
* - `declared` — audited: the action list below is complete and enforced by a
|
|
1010
|
+
* conformance test against the dispatch chain.
|
|
1011
|
+
* - `undeclared` — routed, but not audited. Craft claims nothing either way;
|
|
1012
|
+
* treat it as "try it and handle failure", not as "missing".
|
|
1013
|
+
* - `unavailable` — reachable and known not to work. `reason` says why.
|
|
1014
|
+
* - `unrouted` — implemented natively but absent from the dispatcher, so no
|
|
1015
|
+
* message can reach it.
|
|
1016
|
+
*/
|
|
1017
|
+
export type CapabilityNamespaceStatus = 'declared' | 'undeclared' | 'unavailable' | 'unrouted';
|
|
1018
|
+
export interface CapabilityAction {
|
|
1019
|
+
status: 'live' | 'unavailable';
|
|
1020
|
+
/** Whether native sends a reply the caller is waiting on. */
|
|
1021
|
+
reply: 'none' | 'result';
|
|
1022
|
+
/** Present when the action is unavailable. */
|
|
1023
|
+
reason?: string;
|
|
1024
|
+
}
|
|
1025
|
+
export interface CapabilityNamespace {
|
|
1026
|
+
status: CapabilityNamespaceStatus;
|
|
1027
|
+
reason?: string;
|
|
1028
|
+
/** Present only for `declared` namespaces. */
|
|
1029
|
+
actions?: Record<string, CapabilityAction>;
|
|
1030
|
+
}
|
|
1031
|
+
/**
|
|
1032
|
+
* What the native binary behind this page actually serves.
|
|
1033
|
+
*
|
|
1034
|
+
* The injected bridge script is one blob, the same in every build, so the
|
|
1035
|
+
* presence of `window.craft.x` has never been evidence that anything is behind
|
|
1036
|
+
* it. This is the evidence.
|
|
1037
|
+
*/
|
|
1038
|
+
export interface CraftCapabilities {
|
|
1039
|
+
namespaces: Record<string, CapabilityNamespace>;
|
|
1040
|
+
/**
|
|
1041
|
+
* Every `craft:*` event channel, and whether anything native emits on it.
|
|
1042
|
+
*
|
|
1043
|
+
* A `false` here means subscribing would work and never fire — which cannot
|
|
1044
|
+
* be derived from the action tables, so it is tracked separately by the
|
|
1045
|
+
* emitters themselves.
|
|
1046
|
+
*/
|
|
1047
|
+
channels: Record<string, boolean>;
|
|
1048
|
+
}
|
|
1049
|
+
/**
|
|
1050
|
+
* The only value types `craft.prefs` stores.
|
|
1051
|
+
*
|
|
1052
|
+
* Anything else is refused in the page, before it can reach native. That is
|
|
1053
|
+
* deliberate rather than a limitation dodged: the preferences API raises an
|
|
1054
|
+
* Objective-C exception for a value that is not a property-list type, and Zig
|
|
1055
|
+
* cannot catch one — so refusing containers here is what makes that crash
|
|
1056
|
+
* unreachable. Serialise structure yourself:
|
|
1057
|
+
* `prefs.set(k, JSON.stringify(v))`.
|
|
1058
|
+
*/
|
|
1059
|
+
export type PreferenceValue = string | number | boolean;
|
|
1060
|
+
export interface CraftPreferencesInfo {
|
|
1061
|
+
/**
|
|
1062
|
+
* The preferences domain in use — the bundle identifier inside a packaged
|
|
1063
|
+
* `.app`, the executable's name otherwise.
|
|
1064
|
+
*/
|
|
1065
|
+
domain: string;
|
|
1066
|
+
/** The key prefix craft namespaces its own preferences under. */
|
|
1067
|
+
prefix: string;
|
|
1068
|
+
/** How many keys the app currently has stored. */
|
|
1069
|
+
count: number;
|
|
1070
|
+
/** A copy-pasteable command that prints the domain. */
|
|
1071
|
+
readCommand: string;
|
|
1072
|
+
}
|
|
1073
|
+
/**
|
|
1074
|
+
* A small preference store over the platform's own mechanism, so `defaults
|
|
1075
|
+
* read` and a native settings pane both see what the app wrote.
|
|
1076
|
+
*
|
|
1077
|
+
* macOS only. Values are stored as native property-list types under a reserved
|
|
1078
|
+
* key prefix, so clearing craft's preferences cannot disturb the AppKit and
|
|
1079
|
+
* WebKit keys that share the same domain.
|
|
1080
|
+
*/
|
|
1081
|
+
export interface CraftPreferencesAPI {
|
|
1082
|
+
/**
|
|
1083
|
+
* Read a preference.
|
|
1084
|
+
*
|
|
1085
|
+
* `fallback` is returned when the key is absent — and also when what is
|
|
1086
|
+
* stored is of a different type from the fallback, which is how a value left
|
|
1087
|
+
* behind by an older build of the app degrades to the default rather than to
|
|
1088
|
+
* a surprise. Call `get(key)` with no fallback to read whatever is stored.
|
|
1089
|
+
*
|
|
1090
|
+
* Rejects with `code: 'PREFS_FOREIGN_VALUE'` if something outside craft
|
|
1091
|
+
* wrote a value craft cannot represent, rather than coercing it away.
|
|
1092
|
+
*/
|
|
1093
|
+
get: <T extends PreferenceValue>(key: string, fallback?: T) => Promise<T | undefined>;
|
|
1094
|
+
/**
|
|
1095
|
+
* Write a preference. Resolves once the value is on disk, not merely once
|
|
1096
|
+
* the message was posted.
|
|
1097
|
+
*
|
|
1098
|
+
* Rejects with `code: 'PREFS_UNSUPPORTED_VALUE'` for anything that is not a
|
|
1099
|
+
* string, number or boolean; `'PREFS_NON_FINITE'` for NaN or Infinity;
|
|
1100
|
+
* `'PREFS_VALUE_TOO_LARGE'` past 8 KiB; `'PREFS_BAD_KEY'` for a key outside
|
|
1101
|
+
* `/^[A-Za-z0-9][A-Za-z0-9_.-]{0,63}$/`.
|
|
1102
|
+
*/
|
|
1103
|
+
set: (key: string, value: PreferenceValue) => Promise<void>;
|
|
1104
|
+
/** Remove a preference. Resolves to whether it was there to begin with. */
|
|
1105
|
+
delete: (key: string) => Promise<boolean>;
|
|
1106
|
+
/**
|
|
1107
|
+
* Remove every preference this app has set, and nothing else. Resolves to
|
|
1108
|
+
* how many were removed.
|
|
1109
|
+
*/
|
|
1110
|
+
clear: () => Promise<number>;
|
|
1111
|
+
/** Every key this app has set, sorted. */
|
|
1112
|
+
keys: () => Promise<string[]>;
|
|
1113
|
+
/**
|
|
1114
|
+
* Which domain the preferences are actually landing in.
|
|
1115
|
+
*
|
|
1116
|
+
* Worth having: an unbundled dev-mode binary writes to a domain named after
|
|
1117
|
+
* the executable while a packaged `.app` writes to its bundle identifier, so
|
|
1118
|
+
* preferences set during development can appear to vanish once the app is
|
|
1119
|
+
* packaged.
|
|
1120
|
+
*/
|
|
1121
|
+
info: () => Promise<CraftPreferencesInfo>;
|
|
1122
|
+
}
|
|
1123
|
+
/**
|
|
1124
|
+
* The Cmd+, convention.
|
|
1125
|
+
*
|
|
1126
|
+
* craft's default App menu ships a `Settings…` item, and this is where its
|
|
1127
|
+
* click arrives. An app that replaces the whole menu bar with
|
|
1128
|
+
* `craft.menu.set()` loses the default item, and can declare
|
|
1129
|
+
* `{ id: 'settings', role: 'settings', label: 'Settings…', shortcut: 'cmd+,' }`
|
|
1130
|
+
* to get it back — the same handler answers either way.
|
|
1131
|
+
*/
|
|
1132
|
+
export interface CraftSettingsAPI {
|
|
1133
|
+
/** Subscribe to Settings being opened. Returns an unsubscribe function. */
|
|
1134
|
+
onOpen: (handler: (event: {
|
|
1135
|
+
source: string;
|
|
1136
|
+
}) => void) => () => void;
|
|
1137
|
+
/**
|
|
1138
|
+
* Open settings from the app's own UI — a gear button, say — so it lands in
|
|
1139
|
+
* the same handler as Cmd+, rather than needing a second code path. Purely
|
|
1140
|
+
* page-local; nothing crosses the bridge.
|
|
1141
|
+
*/
|
|
1142
|
+
open: (source?: string) => Promise<void>;
|
|
1143
|
+
}
|
|
1144
|
+
/** A global hotkey, as `craft.shortcuts.list()` reports it. */
|
|
1145
|
+
export interface GlobalShortcut {
|
|
1146
|
+
/** The id the app registered it under. */
|
|
1147
|
+
id: string;
|
|
1148
|
+
/**
|
|
1149
|
+
* Craft's canonical spelling of the combination — `Cmd+Delete`, whatever
|
|
1150
|
+
* the app originally wrote — so it can be passed straight back to
|
|
1151
|
+
* `register()`.
|
|
1152
|
+
*/
|
|
1153
|
+
accelerator: string;
|
|
1154
|
+
/** The key alone, canonically named. */
|
|
1155
|
+
key: string;
|
|
1156
|
+
/** False while `disable()` has the key released back to the system. */
|
|
1157
|
+
enabled: boolean;
|
|
1158
|
+
}
|
|
1159
|
+
/** Why a registration was refused. */
|
|
1160
|
+
export interface GlobalShortcutError {
|
|
1161
|
+
/** The id from the payload that failed, or `''` if it had none. */
|
|
1162
|
+
id: string;
|
|
1163
|
+
/** Craft's error code, e.g. `NATIVE_CALL_FAILED`, `INVALID_PARAMETER`. */
|
|
1164
|
+
code: string;
|
|
1165
|
+
message: string;
|
|
1166
|
+
}
|
|
1167
|
+
/**
|
|
1168
|
+
* System-wide hotkeys: they fire whether or not the app is frontmost.
|
|
1169
|
+
*
|
|
1170
|
+
* Accelerators are `+`-separated and case-insensitive — `'Cmd+Shift+H'`. The
|
|
1171
|
+
* last component is the key, everything before it a modifier (`Cmd`/`Command`/
|
|
1172
|
+
* `Meta`, `Ctrl`/`Control`, `Alt`/`Option`, `Shift`, or `CmdOrCtrl` for the
|
|
1173
|
+
* platform's own). Keys are named by position on the keyboard, not by the
|
|
1174
|
+
* character they produce, so a binding survives a layout change.
|
|
1175
|
+
*
|
|
1176
|
+
* At least one of Command, Control or Option is required, except on the
|
|
1177
|
+
* function keys: a bare global hotkey on `H` would mean no application on the
|
|
1178
|
+
* system ever saw the user type an h again.
|
|
1179
|
+
*/
|
|
1180
|
+
export interface CraftGlobalShortcutsAPI {
|
|
1181
|
+
/**
|
|
1182
|
+
* Reserve a combination system-wide. Registering an `id` twice replaces the
|
|
1183
|
+
* first binding.
|
|
1184
|
+
*
|
|
1185
|
+
* The promise resolves once the message is posted, **not** once the key is
|
|
1186
|
+
* reserved — so a combination that belongs to another app or to the system
|
|
1187
|
+
* resolves here and reports on {@link CraftGlobalShortcutsAPI.onError}.
|
|
1188
|
+
*/
|
|
1189
|
+
register: (id: string, accelerator: string) => Promise<void>;
|
|
1190
|
+
/** Give the key back to the system and forget the shortcut. */
|
|
1191
|
+
unregister: (id: string) => Promise<void>;
|
|
1192
|
+
/** The same, for every shortcut this app holds. */
|
|
1193
|
+
unregisterAll: () => Promise<void>;
|
|
1194
|
+
/**
|
|
1195
|
+
* Stop firing *and release the key*, so other apps can use it again. The
|
|
1196
|
+
* shortcut stays listed. Holding a reservation while dropping the event
|
|
1197
|
+
* would make the combination dead in every other app for as long as Craft
|
|
1198
|
+
* ran.
|
|
1199
|
+
*/
|
|
1200
|
+
disable: (id: string) => Promise<void>;
|
|
1201
|
+
/**
|
|
1202
|
+
* Take the key back. Can fail if something else claimed it while it was
|
|
1203
|
+
* released, which reports on {@link CraftGlobalShortcutsAPI.onError}.
|
|
1204
|
+
*/
|
|
1205
|
+
enable: (id: string) => Promise<void>;
|
|
1206
|
+
/** Whether the id is known — enabled or not. */
|
|
1207
|
+
isRegistered: (id: string) => Promise<boolean>;
|
|
1208
|
+
list: () => Promise<GlobalShortcut[]>;
|
|
1209
|
+
/** Every press of a registered, enabled shortcut. Returns an unsubscribe. */
|
|
1210
|
+
on: (handler: (event: {
|
|
1211
|
+
id: string;
|
|
1212
|
+
accelerator: string;
|
|
1213
|
+
}) => void) => () => void;
|
|
1214
|
+
/**
|
|
1215
|
+
* Where every fire-and-forget call reports its failure — `register`,
|
|
1216
|
+
* `enable`, `disable`, `unregister`. (`isRegistered` and `list` are
|
|
1217
|
+
* requests and reject their own promise instead.) Returns an unsubscribe.
|
|
1218
|
+
*/
|
|
1219
|
+
onError: (handler: (error: GlobalShortcutError) => void) => () => void;
|
|
926
1220
|
}
|
|
927
1221
|
/**
|
|
928
1222
|
* Focus / Do Not Disturb authorization state, mirroring
|