@trayai/tray-sync-cli 1.0.13 → 1.0.14
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/api/resources/me.js +14 -0
- package/dist/commands/api/api.js +4 -0
- package/dist/commands/api/login.js +91 -0
- package/dist/commands/api/logout.js +39 -0
- package/dist/commands/api/project.js +2 -2
- package/dist/commands/api/shared.js +10 -4
- package/dist/commands/api/workflow.js +4 -4
- package/dist/lib/apiAuth.js +25 -2
- package/dist/lib/apiSessionStore.js +104 -0
- package/dist/lib/browserOpener.js +24 -0
- package/dist/lib/dotenvEdit.js +31 -0
- package/dist/lib/oauth.js +125 -0
- package/dist/lib/oauthCallbackServer.js +97 -0
- package/dist/lib/oauthRefresh.js +65 -0
- package/dist/lib/region.js +1 -0
- package/node_modules/bundle-name/index.js +5 -0
- package/node_modules/bundle-name/license +9 -0
- package/node_modules/bundle-name/package.json +45 -0
- package/node_modules/bundle-name/readme.md +23 -0
- package/node_modules/default-browser/index.d.ts +29 -0
- package/node_modules/default-browser/index.js +34 -0
- package/node_modules/default-browser/license +9 -0
- package/node_modules/default-browser/package.json +54 -0
- package/node_modules/default-browser/readme.md +24 -0
- package/node_modules/default-browser/windows.js +59 -0
- package/node_modules/default-browser-id/index.js +25 -0
- package/node_modules/default-browser-id/license +9 -0
- package/node_modules/default-browser-id/package.json +41 -0
- package/node_modules/default-browser-id/readme.md +19 -0
- package/node_modules/define-lazy-prop/index.d.ts +31 -0
- package/node_modules/define-lazy-prop/index.js +18 -0
- package/node_modules/define-lazy-prop/license +9 -0
- package/node_modules/define-lazy-prop/package.json +51 -0
- package/node_modules/define-lazy-prop/readme.md +55 -0
- package/node_modules/dotenv/CHANGELOG.md +643 -0
- package/node_modules/dotenv/LICENSE +23 -0
- package/node_modules/dotenv/README-es.md +757 -0
- package/node_modules/dotenv/README.md +812 -0
- package/node_modules/dotenv/SECURITY.md +1 -0
- package/node_modules/dotenv/config.d.ts +1 -0
- package/node_modules/dotenv/config.js +9 -0
- package/node_modules/dotenv/lib/cli-options.js +17 -0
- package/node_modules/dotenv/lib/env-options.js +28 -0
- package/node_modules/dotenv/lib/main.d.ts +179 -0
- package/node_modules/dotenv/lib/main.js +423 -0
- package/node_modules/dotenv/package.json +62 -0
- package/node_modules/dotenv/skills/dotenv/SKILL.md +200 -0
- package/node_modules/dotenv/skills/dotenvx/SKILL.md +118 -0
- package/node_modules/graceful-fs/LICENSE +15 -0
- package/node_modules/graceful-fs/README.md +143 -0
- package/node_modules/graceful-fs/clone.js +23 -0
- package/node_modules/graceful-fs/graceful-fs.js +448 -0
- package/node_modules/graceful-fs/legacy-streams.js +118 -0
- package/node_modules/graceful-fs/package.json +53 -0
- package/node_modules/graceful-fs/polyfills.js +355 -0
- package/node_modules/is-docker/cli.js +5 -0
- package/node_modules/is-docker/index.d.ts +13 -0
- package/node_modules/is-docker/index.js +29 -0
- package/node_modules/is-docker/license +9 -0
- package/node_modules/is-docker/package.json +44 -0
- package/node_modules/is-docker/readme.md +27 -0
- package/node_modules/is-in-ssh/index.d.ts +15 -0
- package/node_modules/is-in-ssh/index.js +7 -0
- package/node_modules/is-in-ssh/license +9 -0
- package/node_modules/is-in-ssh/package.json +43 -0
- package/node_modules/is-in-ssh/readme.md +43 -0
- package/node_modules/is-inside-container/cli.js +5 -0
- package/node_modules/is-inside-container/index.d.ts +13 -0
- package/node_modules/is-inside-container/index.js +23 -0
- package/node_modules/is-inside-container/license +9 -0
- package/node_modules/is-inside-container/package.json +51 -0
- package/node_modules/is-inside-container/readme.md +36 -0
- package/node_modules/is-wsl/index.d.ts +15 -0
- package/node_modules/is-wsl/index.js +36 -0
- package/node_modules/is-wsl/license +9 -0
- package/node_modules/is-wsl/package.json +58 -0
- package/node_modules/is-wsl/readme.md +21 -0
- package/node_modules/open/index.d.ts +158 -0
- package/node_modules/open/index.js +425 -0
- package/node_modules/open/license +9 -0
- package/node_modules/open/package.json +70 -0
- package/node_modules/open/readme.md +197 -0
- package/node_modules/open/xdg-open +1267 -0
- package/node_modules/powershell-utils/index.d.ts +175 -0
- package/node_modules/powershell-utils/index.js +79 -0
- package/node_modules/powershell-utils/license +9 -0
- package/node_modules/powershell-utils/package.json +47 -0
- package/node_modules/powershell-utils/readme.md +209 -0
- package/node_modules/proper-lockfile/CHANGELOG.md +108 -0
- package/node_modules/proper-lockfile/LICENSE +21 -0
- package/node_modules/proper-lockfile/README.md +183 -0
- package/node_modules/proper-lockfile/index.js +40 -0
- package/node_modules/proper-lockfile/lib/adapter.js +85 -0
- package/node_modules/proper-lockfile/lib/lockfile.js +342 -0
- package/node_modules/proper-lockfile/lib/mtime-precision.js +55 -0
- package/node_modules/proper-lockfile/package.json +71 -0
- package/node_modules/retry/.npmignore +3 -0
- package/node_modules/retry/.travis.yml +15 -0
- package/node_modules/retry/License +21 -0
- package/node_modules/retry/Makefile +18 -0
- package/node_modules/retry/README.md +227 -0
- package/node_modules/retry/equation.gif +0 -0
- package/node_modules/retry/example/dns.js +31 -0
- package/node_modules/retry/example/stop.js +40 -0
- package/node_modules/retry/index.js +1 -0
- package/node_modules/retry/lib/retry.js +100 -0
- package/node_modules/retry/lib/retry_operation.js +158 -0
- package/node_modules/retry/package.json +32 -0
- package/node_modules/retry/test/common.js +10 -0
- package/node_modules/retry/test/integration/test-forever.js +24 -0
- package/node_modules/retry/test/integration/test-retry-operation.js +258 -0
- package/node_modules/retry/test/integration/test-retry-wrap.js +101 -0
- package/node_modules/retry/test/integration/test-timeouts.js +69 -0
- package/node_modules/run-applescript/index.d.ts +66 -0
- package/node_modules/run-applescript/index.js +37 -0
- package/node_modules/run-applescript/license +9 -0
- package/node_modules/run-applescript/package.json +42 -0
- package/node_modules/run-applescript/readme.md +78 -0
- package/node_modules/signal-exit/LICENSE.txt +16 -0
- package/node_modules/signal-exit/README.md +39 -0
- package/node_modules/signal-exit/index.js +202 -0
- package/node_modules/signal-exit/package.json +38 -0
- package/node_modules/signal-exit/signals.js +53 -0
- package/node_modules/wsl-utils/index.d.ts +180 -0
- package/node_modules/wsl-utils/index.js +145 -0
- package/node_modules/wsl-utils/license +9 -0
- package/node_modules/wsl-utils/node_modules/powershell-utils/index.d.ts +122 -0
- package/node_modules/wsl-utils/node_modules/powershell-utils/index.js +58 -0
- package/node_modules/wsl-utils/node_modules/powershell-utils/license +9 -0
- package/node_modules/wsl-utils/node_modules/powershell-utils/package.json +47 -0
- package/node_modules/wsl-utils/node_modules/powershell-utils/readme.md +153 -0
- package/node_modules/wsl-utils/package.json +48 -0
- package/node_modules/wsl-utils/readme.md +223 -0
- package/node_modules/wsl-utils/utilities.js +19 -0
- package/package.json +8 -1
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { createServer } from "node:http";
|
|
2
|
+
export const DEFAULT_CALLBACK_TIMEOUT_MS = 5 * 60 * 1000;
|
|
3
|
+
const CALLBACK_PATH = "/callback";
|
|
4
|
+
function page(title, detail) {
|
|
5
|
+
return `<!doctype html>
|
|
6
|
+
<html lang="en">
|
|
7
|
+
<head><meta charset="utf-8"><title>${title}</title></head>
|
|
8
|
+
<body style="font-family: system-ui, sans-serif; max-width: 32rem; margin: 4rem auto; text-align: center;">
|
|
9
|
+
<h1 style="font-size: 1.4rem;">${title}</h1>
|
|
10
|
+
<p>${detail}</p>
|
|
11
|
+
</body>
|
|
12
|
+
</html>`;
|
|
13
|
+
}
|
|
14
|
+
export async function startCallbackServer(options) {
|
|
15
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_CALLBACK_TIMEOUT_MS;
|
|
16
|
+
let settle = null;
|
|
17
|
+
const result = new Promise((resolve, reject) => {
|
|
18
|
+
settle = { resolve, reject };
|
|
19
|
+
});
|
|
20
|
+
result.catch(() => { });
|
|
21
|
+
let done = false;
|
|
22
|
+
const finish = (outcome) => {
|
|
23
|
+
if (done || !settle)
|
|
24
|
+
return;
|
|
25
|
+
done = true;
|
|
26
|
+
clearTimeout(timer);
|
|
27
|
+
options.signal?.removeEventListener("abort", onAbort);
|
|
28
|
+
if (outcome.code !== undefined)
|
|
29
|
+
settle.resolve(outcome.code);
|
|
30
|
+
else
|
|
31
|
+
settle.reject(outcome.error ?? new Error("Login was cancelled."));
|
|
32
|
+
};
|
|
33
|
+
const timer = setTimeout(() => {
|
|
34
|
+
finish({
|
|
35
|
+
error: new Error(`Timed out waiting for the browser sign-in (no callback after ${Math.round(timeoutMs / 60_000)} minutes). Run \`tray api login\` to try again.`),
|
|
36
|
+
});
|
|
37
|
+
}, timeoutMs);
|
|
38
|
+
timer.unref();
|
|
39
|
+
const onAbort = () => finish({ error: new Error("Login was cancelled.") });
|
|
40
|
+
options.signal?.addEventListener("abort", onAbort, { once: true });
|
|
41
|
+
const server = createServer((req, res) => {
|
|
42
|
+
const url = new URL(req.url ?? "/", "http://127.0.0.1");
|
|
43
|
+
if (url.pathname !== CALLBACK_PATH) {
|
|
44
|
+
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
45
|
+
res.end("Not found");
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (done) {
|
|
49
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
50
|
+
res.end(page("Already handled", "This sign-in attempt has already completed. Return to your terminal."));
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const state = url.searchParams.get("state");
|
|
54
|
+
if (state !== options.expectedState) {
|
|
55
|
+
res.writeHead(400, { "Content-Type": "text/html" });
|
|
56
|
+
res.end(page("Sign-in failed", "The response could not be verified. Return to your terminal and try again."));
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const errorParam = url.searchParams.get("error");
|
|
60
|
+
if (errorParam) {
|
|
61
|
+
const description = url.searchParams.get("error_description") ?? "";
|
|
62
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
63
|
+
res.end(page("Sign-in failed", "Return to your terminal for details."));
|
|
64
|
+
finish({
|
|
65
|
+
error: new Error(`Tray sign-in was not completed (${errorParam}${description ? `: ${description}` : ""}).`),
|
|
66
|
+
});
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const code = url.searchParams.get("code");
|
|
70
|
+
if (!code) {
|
|
71
|
+
res.writeHead(400, { "Content-Type": "text/html" });
|
|
72
|
+
res.end(page("Sign-in failed", "The response could not be verified. Return to your terminal and try again."));
|
|
73
|
+
finish({
|
|
74
|
+
error: new Error("The sign-in response was missing the authorization code. Run `tray api login` to try again."),
|
|
75
|
+
});
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
79
|
+
res.end(page("Signed in to Tray CLI", "You can close this tab and return to your terminal."));
|
|
80
|
+
finish({ code });
|
|
81
|
+
});
|
|
82
|
+
await new Promise((resolve, reject) => {
|
|
83
|
+
server.once("error", reject);
|
|
84
|
+
server.listen(0, "127.0.0.1", () => resolve());
|
|
85
|
+
});
|
|
86
|
+
const { port } = server.address();
|
|
87
|
+
return {
|
|
88
|
+
redirectUri: `http://127.0.0.1:${port}${CALLBACK_PATH}`,
|
|
89
|
+
waitForCode: () => result,
|
|
90
|
+
close: () => new Promise((resolve) => {
|
|
91
|
+
finish({ error: new Error("Login was cancelled.") });
|
|
92
|
+
server.closeAllConnections();
|
|
93
|
+
server.close(() => resolve());
|
|
94
|
+
}),
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=oauthCallbackServer.js.map
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { refreshAccessToken, OAuthTokenError } from "./oauth.js";
|
|
2
|
+
export const EXPIRY_SKEW_MS = 5 * 60 * 1000;
|
|
3
|
+
export function isFresh(session, nowMs) {
|
|
4
|
+
return session.expiresAt !== undefined && nowMs < session.expiresAt - EXPIRY_SKEW_MS;
|
|
5
|
+
}
|
|
6
|
+
export function toApiSession(region, pair, nowMs) {
|
|
7
|
+
return {
|
|
8
|
+
token: pair.accessToken,
|
|
9
|
+
region,
|
|
10
|
+
refreshToken: pair.refreshToken,
|
|
11
|
+
expiresAt: nowMs + pair.expiresInSeconds * 1000,
|
|
12
|
+
method: "oauth",
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export class ReauthRequiredError extends Error {
|
|
16
|
+
constructor(message) {
|
|
17
|
+
super(message);
|
|
18
|
+
this.name = "ReauthRequiredError";
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export async function getValidApiSession(store, deps = {}) {
|
|
22
|
+
const now = deps.now ?? Date.now;
|
|
23
|
+
const session = await store.get();
|
|
24
|
+
if (!session)
|
|
25
|
+
return null;
|
|
26
|
+
if (!session.refreshToken || session.expiresAt === undefined) {
|
|
27
|
+
return { token: session.token, region: session.region };
|
|
28
|
+
}
|
|
29
|
+
if (isFresh(session, now()))
|
|
30
|
+
return { token: session.token, region: session.region };
|
|
31
|
+
return refreshStoredSession(store, deps, { staleAccessToken: session.token });
|
|
32
|
+
}
|
|
33
|
+
export async function refreshStoredSession(store, deps = {}, options = {}) {
|
|
34
|
+
const refreshGrant = deps.refreshGrant ?? refreshAccessToken;
|
|
35
|
+
const now = deps.now ?? Date.now;
|
|
36
|
+
return store.withLock(async () => {
|
|
37
|
+
const session = await store.get();
|
|
38
|
+
if (!session)
|
|
39
|
+
throw new ReauthRequiredError('Not logged in. Run "tray api login".');
|
|
40
|
+
if (!session.refreshToken) {
|
|
41
|
+
throw new ReauthRequiredError('Your tray api login has expired. Run "tray api login".');
|
|
42
|
+
}
|
|
43
|
+
if (options.staleAccessToken !== undefined) {
|
|
44
|
+
if (session.token !== options.staleAccessToken)
|
|
45
|
+
return { token: session.token, region: session.region };
|
|
46
|
+
}
|
|
47
|
+
else if (isFresh(session, now())) {
|
|
48
|
+
return { token: session.token, region: session.region };
|
|
49
|
+
}
|
|
50
|
+
let pair;
|
|
51
|
+
try {
|
|
52
|
+
pair = await refreshGrant({ region: session.region, refreshToken: session.refreshToken });
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
if (err instanceof OAuthTokenError && err.requiresReauth) {
|
|
56
|
+
await store.clear();
|
|
57
|
+
throw new ReauthRequiredError('Your tray api login has expired or been revoked. Run "tray api login".');
|
|
58
|
+
}
|
|
59
|
+
throw err;
|
|
60
|
+
}
|
|
61
|
+
await store.set(toApiSession(session.region, pair, now()));
|
|
62
|
+
return { token: pair.accessToken, region: session.region };
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=oauthRefresh.js.map
|
package/dist/lib/region.js
CHANGED
|
@@ -6,6 +6,7 @@ function buildRegionUrls(region) {
|
|
|
6
6
|
apiBaseUrl: `https://api.${domain}`,
|
|
7
7
|
graphqlUrl: `https://${domain}/graphql`,
|
|
8
8
|
weaverUrl: `https://weaver.${domain}`,
|
|
9
|
+
authBaseUrl: `https://auth.${domain}`,
|
|
9
10
|
};
|
|
10
11
|
}
|
|
11
12
|
const REGION_URLS = Object.fromEntries(REGIONS.map((region) => [region, buildRegionUrls(region)]));
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import {runAppleScript} from 'run-applescript';
|
|
2
|
+
|
|
3
|
+
export default async function bundleName(bundleId) {
|
|
4
|
+
return runAppleScript(`tell application "Finder" to set app_path to application file id "${bundleId}" as string\ntell application "System Events" to get value of property list item "CFBundleName" of property list file (app_path & ":Contents:Info.plist")`);
|
|
5
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
+
|
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "bundle-name",
|
|
3
|
+
"version": "4.1.0",
|
|
4
|
+
"description": "Get bundle name from a bundle identifier (macOS): `com.apple.Safari` → `Safari`",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": "sindresorhus/bundle-name",
|
|
7
|
+
"funding": "https://github.com/sponsors/sindresorhus",
|
|
8
|
+
"author": {
|
|
9
|
+
"name": "Sindre Sorhus",
|
|
10
|
+
"email": "sindresorhus@gmail.com",
|
|
11
|
+
"url": "https://sindresorhus.com"
|
|
12
|
+
},
|
|
13
|
+
"type": "module",
|
|
14
|
+
"exports": "./index.js",
|
|
15
|
+
"sideEffects": false,
|
|
16
|
+
"engines": {
|
|
17
|
+
"node": ">=18"
|
|
18
|
+
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"test": "xo && ava"
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"index.js"
|
|
24
|
+
],
|
|
25
|
+
"keywords": [
|
|
26
|
+
"macos",
|
|
27
|
+
"plist",
|
|
28
|
+
"applescript",
|
|
29
|
+
"bundle",
|
|
30
|
+
"bundleid",
|
|
31
|
+
"bundlename",
|
|
32
|
+
"id",
|
|
33
|
+
"identifier",
|
|
34
|
+
"CFBundleName",
|
|
35
|
+
"CFBundleIdentifier",
|
|
36
|
+
"uti"
|
|
37
|
+
],
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"run-applescript": "^7.0.0"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"ava": "^6.0.1",
|
|
43
|
+
"xo": "^0.56.0"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# bundle-name
|
|
2
|
+
|
|
3
|
+
> Get [bundle name](https://developer.apple.com/library/Mac/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html#//apple_ref/doc/plist/info/CFBundleName) from a [bundle identifier](https://developer.apple.com/library/Mac/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html#//apple_ref/doc/plist/info/CFBundleIdentifier) (macOS): `com.apple.Safari` → `Safari`
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npm install bundle-name
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```js
|
|
14
|
+
import bundleName from 'bundle-name';
|
|
15
|
+
|
|
16
|
+
console.log(await bundleName('com.apple.Safari'));
|
|
17
|
+
//=> 'Safari'
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Related
|
|
21
|
+
|
|
22
|
+
- [bundle-name-cli](https://github.com/sindresorhus/bundle-name-cli) - CLI for this module
|
|
23
|
+
- [bundle-id](https://github.com/sindresorhus/bundle-id) - Get bundle identifier from a bundle name
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export type Browser = {
|
|
2
|
+
/**
|
|
3
|
+
Human-readadable name of the browser.
|
|
4
|
+
*/
|
|
5
|
+
name: string;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
The unique identifier for the browser on the current platform:
|
|
9
|
+
- On macOS, it's the app's bundle identifier.
|
|
10
|
+
- On Linux, it's the desktop file identifier (from `xdg-mime`).
|
|
11
|
+
- On Windows, it's an invented identifier, because apps on Windows does not have identifiers.
|
|
12
|
+
*/
|
|
13
|
+
id: string;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
Get the default browser for the current platform.
|
|
18
|
+
|
|
19
|
+
@returns A promise for the browser.
|
|
20
|
+
|
|
21
|
+
@example
|
|
22
|
+
```
|
|
23
|
+
import defaultBrowser from 'default-browser';
|
|
24
|
+
|
|
25
|
+
console.log(await defaultBrowser());
|
|
26
|
+
//=> {name: 'Safari', id: 'com.apple.Safari'}
|
|
27
|
+
```
|
|
28
|
+
*/
|
|
29
|
+
export default function defaultBrowser(): Promise<Browser>;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import {promisify} from 'node:util';
|
|
2
|
+
import process from 'node:process';
|
|
3
|
+
import {execFile} from 'node:child_process';
|
|
4
|
+
import defaultBrowserId from 'default-browser-id';
|
|
5
|
+
import bundleName from 'bundle-name';
|
|
6
|
+
import windows from './windows.js';
|
|
7
|
+
|
|
8
|
+
export {_windowsBrowserProgIdMap} from './windows.js';
|
|
9
|
+
|
|
10
|
+
const execFileAsync = promisify(execFile);
|
|
11
|
+
|
|
12
|
+
// Inlined: https://github.com/sindresorhus/titleize/blob/main/index.js
|
|
13
|
+
const titleize = string => string.toLowerCase().replaceAll(/(?:^|\s|-)\S/g, x => x.toUpperCase());
|
|
14
|
+
|
|
15
|
+
export default async function defaultBrowser() {
|
|
16
|
+
if (process.platform === 'darwin') {
|
|
17
|
+
const id = await defaultBrowserId();
|
|
18
|
+
const name = await bundleName(id);
|
|
19
|
+
return {name, id};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (process.platform === 'linux') {
|
|
23
|
+
const {stdout} = await execFileAsync('xdg-mime', ['query', 'default', 'x-scheme-handler/http']);
|
|
24
|
+
const id = stdout.trim();
|
|
25
|
+
const name = titleize(id.replace(/.desktop$/, '').replace('-', ' '));
|
|
26
|
+
return {name, id};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (process.platform === 'win32') {
|
|
30
|
+
return windows();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
throw new Error('Only macOS, Linux, and Windows are supported');
|
|
34
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
+
|
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "default-browser",
|
|
3
|
+
"version": "5.5.1",
|
|
4
|
+
"description": "Get the default browser",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": "sindresorhus/default-browser",
|
|
7
|
+
"funding": "https://github.com/sponsors/sindresorhus",
|
|
8
|
+
"author": {
|
|
9
|
+
"name": "Sindre Sorhus",
|
|
10
|
+
"email": "sindresorhus@gmail.com",
|
|
11
|
+
"url": "https://sindresorhus.com"
|
|
12
|
+
},
|
|
13
|
+
"type": "module",
|
|
14
|
+
"exports": {
|
|
15
|
+
"types": "./index.d.ts",
|
|
16
|
+
"default": "./index.js"
|
|
17
|
+
},
|
|
18
|
+
"sideEffects": false,
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=18"
|
|
21
|
+
},
|
|
22
|
+
"scripts": {
|
|
23
|
+
"test": "xo && ava && tsd"
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"index.js",
|
|
27
|
+
"index.d.ts",
|
|
28
|
+
"windows.js"
|
|
29
|
+
],
|
|
30
|
+
"keywords": [
|
|
31
|
+
"macos",
|
|
32
|
+
"linux",
|
|
33
|
+
"browser",
|
|
34
|
+
"default",
|
|
35
|
+
"plist",
|
|
36
|
+
"web",
|
|
37
|
+
"bundle",
|
|
38
|
+
"bundleid",
|
|
39
|
+
"id",
|
|
40
|
+
"identifier",
|
|
41
|
+
"uti",
|
|
42
|
+
"cfbundlename",
|
|
43
|
+
"applescript"
|
|
44
|
+
],
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"bundle-name": "^4.1.0",
|
|
47
|
+
"default-browser-id": "^5.0.0"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"ava": "^6.0.1",
|
|
51
|
+
"tsd": "^0.30.0",
|
|
52
|
+
"xo": "^0.56.0"
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# default-browser
|
|
2
|
+
|
|
3
|
+
> Get the default browser
|
|
4
|
+
|
|
5
|
+
Works on macOS, Linux, and Windows.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npm install default-browser
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```js
|
|
16
|
+
import defaultBrowser from 'default-browser';
|
|
17
|
+
|
|
18
|
+
console.log(await defaultBrowser());
|
|
19
|
+
//=> {name: 'Safari', id: 'com.apple.Safari'}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Related
|
|
23
|
+
|
|
24
|
+
- [default-browser-cli](https://github.com/sindresorhus/default-browser-cli) - CLI for this package
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import process from 'node:process';
|
|
2
|
+
import {promisify} from 'node:util';
|
|
3
|
+
import {execFile} from 'node:child_process';
|
|
4
|
+
|
|
5
|
+
const execFileAsync = promisify(execFile);
|
|
6
|
+
|
|
7
|
+
// TODO: Fix the casing of bundle identifiers in the next major version.
|
|
8
|
+
|
|
9
|
+
// Windows doesn't have browser IDs in the same way macOS/Linux does so we give fake
|
|
10
|
+
// ones that look real and match the macOS/Linux versions for cross-platform apps.
|
|
11
|
+
const windowsBrowserProgIds = {
|
|
12
|
+
MSEdgeHTM: {name: 'Edge', id: 'com.microsoft.edge'}, // The missing `L` is correct.
|
|
13
|
+
MSEdgeBHTML: {name: 'Edge Beta', id: 'com.microsoft.edge.beta'},
|
|
14
|
+
MSEdgeDHTML: {name: 'Edge Dev', id: 'com.microsoft.edge.dev'},
|
|
15
|
+
AppXq0fevzme2pys62n3e0fbqa7peapykr8v: {name: 'Edge', id: 'com.microsoft.edge.old'},
|
|
16
|
+
ChromeHTML: {name: 'Chrome', id: 'com.google.chrome'},
|
|
17
|
+
ChromeBHTML: {name: 'Chrome Beta', id: 'com.google.chrome.beta'},
|
|
18
|
+
ChromeDHTML: {name: 'Chrome Dev', id: 'com.google.chrome.dev'},
|
|
19
|
+
ChromiumHTM: {name: 'Chromium', id: 'org.chromium.Chromium'},
|
|
20
|
+
BraveHTML: {name: 'Brave', id: 'com.brave.Browser'},
|
|
21
|
+
BraveBHTML: {name: 'Brave Beta', id: 'com.brave.Browser.beta'},
|
|
22
|
+
BraveDHTML: {name: 'Brave Dev', id: 'com.brave.Browser.dev'},
|
|
23
|
+
BraveSSHTM: {name: 'Brave Nightly', id: 'com.brave.Browser.nightly'},
|
|
24
|
+
FirefoxURL: {name: 'Firefox', id: 'org.mozilla.firefox'},
|
|
25
|
+
OperaStable: {name: 'Opera', id: 'com.operasoftware.Opera'},
|
|
26
|
+
VivaldiHTM: {name: 'Vivaldi', id: 'com.vivaldi.Vivaldi'},
|
|
27
|
+
'IE.HTTP': {name: 'Internet Explorer', id: 'com.microsoft.ie'},
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const _windowsBrowserProgIdMap = new Map(Object.entries(windowsBrowserProgIds));
|
|
31
|
+
|
|
32
|
+
export class UnknownBrowserError extends Error {}
|
|
33
|
+
|
|
34
|
+
export default async function defaultBrowser(_execFileAsync = execFileAsync) {
|
|
35
|
+
const regPath = `${process.env.SYSTEMROOT ?? process.env.windir ?? 'C:\\Windows'}\\System32\\reg.exe`;
|
|
36
|
+
const {stdout} = await _execFileAsync(regPath, [
|
|
37
|
+
'QUERY',
|
|
38
|
+
' HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\http\\UserChoice',
|
|
39
|
+
'/v',
|
|
40
|
+
'ProgId',
|
|
41
|
+
]);
|
|
42
|
+
|
|
43
|
+
const match = /ProgId\s*REG_SZ\s*(?<id>\S+)/.exec(stdout);
|
|
44
|
+
if (!match) {
|
|
45
|
+
throw new UnknownBrowserError(`Cannot find Windows browser in stdout: ${JSON.stringify(stdout)}`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const {id} = match.groups;
|
|
49
|
+
|
|
50
|
+
// Windows can append a hash suffix to ProgIds using a dot or hyphen
|
|
51
|
+
// (e.g., `ChromeHTML.ABC123`, `FirefoxURL-6F193CCC56814779`).
|
|
52
|
+
// Try exact match first, then try without the suffix.
|
|
53
|
+
const dotIndex = id.lastIndexOf('.');
|
|
54
|
+
const hyphenIndex = id.lastIndexOf('-');
|
|
55
|
+
const baseIdByDot = dotIndex === -1 ? undefined : id.slice(0, dotIndex);
|
|
56
|
+
const baseIdByHyphen = hyphenIndex === -1 ? undefined : id.slice(0, hyphenIndex);
|
|
57
|
+
|
|
58
|
+
return windowsBrowserProgIds[id] ?? windowsBrowserProgIds[baseIdByDot] ?? windowsBrowserProgIds[baseIdByHyphen] ?? {name: id, id};
|
|
59
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import {promisify} from 'node:util';
|
|
2
|
+
import process from 'node:process';
|
|
3
|
+
import {execFile} from 'node:child_process';
|
|
4
|
+
|
|
5
|
+
const execFileAsync = promisify(execFile);
|
|
6
|
+
|
|
7
|
+
export default async function defaultBrowserId() {
|
|
8
|
+
if (process.platform !== 'darwin') {
|
|
9
|
+
throw new Error('macOS only');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const {stdout} = await execFileAsync('defaults', ['read', 'com.apple.LaunchServices/com.apple.launchservices.secure', 'LSHandlers']);
|
|
13
|
+
|
|
14
|
+
// `(?!-)` is to prevent matching `LSHandlerRoleAll = "-";`.
|
|
15
|
+
const match = /LSHandlerRoleAll = "(?!-)(?<id>[^"]+?)";\s+?LSHandlerURLScheme = (?:http|https);/.exec(stdout);
|
|
16
|
+
|
|
17
|
+
const browserId = match?.groups.id ?? 'com.apple.Safari';
|
|
18
|
+
|
|
19
|
+
// Correct the case for Safari's bundle identifier
|
|
20
|
+
if (browserId === 'com.apple.safari') {
|
|
21
|
+
return 'com.apple.Safari';
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return browserId;
|
|
25
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
+
|
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "default-browser-id",
|
|
3
|
+
"version": "5.0.1",
|
|
4
|
+
"description": "Get the bundle identifier of the default browser (macOS). Example: com.apple.Safari",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": "sindresorhus/default-browser-id",
|
|
7
|
+
"funding": "https://github.com/sponsors/sindresorhus",
|
|
8
|
+
"author": {
|
|
9
|
+
"name": "Sindre Sorhus",
|
|
10
|
+
"email": "sindresorhus@gmail.com",
|
|
11
|
+
"url": "https://sindresorhus.com"
|
|
12
|
+
},
|
|
13
|
+
"type": "module",
|
|
14
|
+
"exports": "./index.js",
|
|
15
|
+
"sideEffects": false,
|
|
16
|
+
"engines": {
|
|
17
|
+
"node": ">=18"
|
|
18
|
+
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"test": "xo && ava"
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"index.js"
|
|
24
|
+
],
|
|
25
|
+
"keywords": [
|
|
26
|
+
"macos",
|
|
27
|
+
"browser",
|
|
28
|
+
"default",
|
|
29
|
+
"plist",
|
|
30
|
+
"web",
|
|
31
|
+
"bundle",
|
|
32
|
+
"bundleid",
|
|
33
|
+
"id",
|
|
34
|
+
"identifier",
|
|
35
|
+
"uti"
|
|
36
|
+
],
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"ava": "^6.0.1",
|
|
39
|
+
"xo": "^0.56.0"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# default-browser-id
|
|
2
|
+
|
|
3
|
+
> Get the [bundle identifier](https://developer.apple.com/library/Mac/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html#//apple_ref/doc/plist/info/CFBundleIdentifier) of the default browser *(macOS)*\
|
|
4
|
+
> Example: `com.apple.Safari`
|
|
5
|
+
|
|
6
|
+
## Install
|
|
7
|
+
|
|
8
|
+
```sh
|
|
9
|
+
npm install default-browser-id
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Usage
|
|
13
|
+
|
|
14
|
+
```js
|
|
15
|
+
import defaultBrowserId from 'default-browser-id';
|
|
16
|
+
|
|
17
|
+
console.log(await defaultBrowserId());
|
|
18
|
+
//=> 'com.apple.Safari'
|
|
19
|
+
```
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
Define a [lazily evaluated](https://en.wikipedia.org/wiki/Lazy_evaluation) property on an object.
|
|
3
|
+
|
|
4
|
+
@param object - Object to add the property to.
|
|
5
|
+
@param propertyName - Name of the property to add.
|
|
6
|
+
@param valueGetter - Called the first time `propertyName` is accessed.
|
|
7
|
+
|
|
8
|
+
@example
|
|
9
|
+
```
|
|
10
|
+
import defineLazyProperty from 'define-lazy-prop';
|
|
11
|
+
|
|
12
|
+
const unicorn = {
|
|
13
|
+
// …
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
defineLazyProperty(unicorn, 'rainbow', () => expensiveComputation());
|
|
17
|
+
|
|
18
|
+
app.on('user-action', () => {
|
|
19
|
+
doSomething(unicorn.rainbow);
|
|
20
|
+
});
|
|
21
|
+
```
|
|
22
|
+
*/
|
|
23
|
+
export default function defineLazyProperty<
|
|
24
|
+
ObjectType extends Record<string, any>,
|
|
25
|
+
PropertyNameType extends string,
|
|
26
|
+
PropertyValueType
|
|
27
|
+
>(
|
|
28
|
+
object: ObjectType,
|
|
29
|
+
propertyName: PropertyNameType,
|
|
30
|
+
valueGetter: () => PropertyValueType
|
|
31
|
+
): ObjectType & {[K in PropertyNameType]: PropertyValueType};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export default function defineLazyProperty(object, propertyName, valueGetter) {
|
|
2
|
+
const define = value => Object.defineProperty(object, propertyName, {value, enumerable: true, writable: true});
|
|
3
|
+
|
|
4
|
+
Object.defineProperty(object, propertyName, {
|
|
5
|
+
configurable: true,
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get() {
|
|
8
|
+
const result = valueGetter();
|
|
9
|
+
define(result);
|
|
10
|
+
return result;
|
|
11
|
+
},
|
|
12
|
+
set(value) {
|
|
13
|
+
define(value);
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
return object;
|
|
18
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
+
|
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|