@nebulr-group/bridge-cli 0.6.6 → 1.0.0-alpha.1
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/README.md +98 -458
- package/dist/api/accounts.d.ts +105 -0
- package/dist/api/accounts.d.ts.map +1 -0
- package/dist/api/accounts.js +58 -0
- package/dist/api/accounts.js.map +1 -0
- package/dist/api/apps.d.ts +50 -0
- package/dist/api/apps.d.ts.map +1 -0
- package/dist/api/apps.js +62 -0
- package/dist/api/apps.js.map +1 -0
- package/dist/api/billing.d.ts +131 -0
- package/dist/api/billing.d.ts.map +1 -0
- package/dist/api/billing.js +42 -0
- package/dist/api/billing.js.map +1 -0
- package/dist/api/flags.d.ts +73 -0
- package/dist/api/flags.d.ts.map +1 -0
- package/dist/api/flags.js +108 -0
- package/dist/api/flags.js.map +1 -0
- package/dist/api/keys.d.ts +48 -0
- package/dist/api/keys.d.ts.map +1 -0
- package/dist/api/keys.js +87 -0
- package/dist/api/keys.js.map +1 -0
- package/dist/auth/browser-open.d.ts +21 -4
- package/dist/auth/browser-open.d.ts.map +1 -1
- package/dist/auth/browser-open.js +45 -65
- package/dist/auth/browser-open.js.map +1 -1
- package/dist/auth/lock.d.ts +8 -0
- package/dist/auth/lock.d.ts.map +1 -0
- package/dist/auth/lock.js +43 -0
- package/dist/auth/lock.js.map +1 -0
- package/dist/auth/loopback.d.ts +8 -12
- package/dist/auth/loopback.d.ts.map +1 -1
- package/dist/auth/loopback.js +40 -45
- package/dist/auth/loopback.js.map +1 -1
- package/dist/auth/session.d.ts +61 -0
- package/dist/auth/session.d.ts.map +1 -0
- package/dist/auth/session.js +142 -0
- package/dist/auth/session.js.map +1 -0
- package/dist/auth/sign-in.d.ts +67 -0
- package/dist/auth/sign-in.d.ts.map +1 -0
- package/dist/auth/sign-in.js +113 -0
- package/dist/auth/sign-in.js.map +1 -0
- package/dist/auth/store.d.ts +89 -0
- package/dist/auth/store.d.ts.map +1 -0
- package/dist/auth/store.js +214 -0
- package/dist/auth/store.js.map +1 -0
- package/dist/bin.js +0 -2
- package/dist/bin.js.map +1 -1
- package/dist/cli.d.ts +1 -2
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +13 -17
- package/dist/cli.js.map +1 -1
- package/dist/client.d.ts +31 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +67 -0
- package/dist/client.js.map +1 -0
- package/dist/commands/accounts.command.d.ts +4 -0
- package/dist/commands/accounts.command.d.ts.map +1 -0
- package/dist/commands/accounts.command.js +166 -0
- package/dist/commands/accounts.command.js.map +1 -0
- package/dist/commands/app.command.d.ts +3 -2
- package/dist/commands/app.command.d.ts.map +1 -1
- package/dist/commands/app.command.js +132 -233
- package/dist/commands/app.command.js.map +1 -1
- package/dist/commands/auth.command.d.ts +2 -12
- package/dist/commands/auth.command.d.ts.map +1 -1
- package/dist/commands/auth.command.js +147 -20
- package/dist/commands/auth.command.js.map +1 -1
- package/dist/commands/billing.command.d.ts +4 -0
- package/dist/commands/billing.command.d.ts.map +1 -0
- package/dist/commands/billing.command.js +185 -0
- package/dist/commands/billing.command.js.map +1 -0
- package/dist/commands/check.command.d.ts +2 -1
- package/dist/commands/check.command.d.ts.map +1 -1
- package/dist/commands/check.command.js +11 -14
- package/dist/commands/check.command.js.map +1 -1
- package/dist/commands/context.d.ts +58 -0
- package/dist/commands/context.d.ts.map +1 -0
- package/dist/commands/context.js +78 -0
- package/dist/commands/context.js.map +1 -0
- package/dist/commands/flag.command.d.ts +3 -62
- package/dist/commands/flag.command.d.ts.map +1 -1
- package/dist/commands/flag.command.js +181 -864
- package/dist/commands/flag.command.js.map +1 -1
- package/dist/commands/key.command.d.ts +4 -0
- package/dist/commands/key.command.d.ts.map +1 -0
- package/dist/commands/key.command.js +44 -0
- package/dist/commands/key.command.js.map +1 -0
- package/dist/commands/plan.command.d.ts +3 -203
- package/dist/commands/plan.command.d.ts.map +1 -1
- package/dist/commands/plan.command.js +200 -759
- package/dist/commands/plan.command.js.map +1 -1
- package/dist/config.d.ts +19 -101
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +51 -364
- package/dist/config.js.map +1 -1
- package/dist/core/discovery.d.ts +27 -0
- package/dist/core/discovery.d.ts.map +1 -0
- package/dist/core/discovery.js +97 -0
- package/dist/core/discovery.js.map +1 -0
- package/dist/core/errors.d.ts +26 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/errors.js +33 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/http.d.ts +23 -0
- package/dist/core/http.d.ts.map +1 -0
- package/dist/core/http.js +153 -0
- package/dist/core/http.js.map +1 -0
- package/dist/core/private-files.d.ts +6 -0
- package/dist/core/private-files.d.ts.map +1 -0
- package/dist/core/private-files.js +96 -0
- package/dist/core/private-files.js.map +1 -0
- package/dist/core/routes.d.ts +100 -0
- package/dist/core/routes.d.ts.map +1 -0
- package/dist/core/routes.js +99 -0
- package/dist/core/routes.js.map +1 -0
- package/dist/core/urls.d.ts +15 -0
- package/dist/core/urls.d.ts.map +1 -0
- package/dist/core/urls.js +55 -0
- package/dist/core/urls.js.map +1 -0
- package/dist/gate-rules.d.ts +1 -2
- package/dist/gate-rules.d.ts.map +1 -1
- package/dist/gate-rules.js +4 -5
- package/dist/gate-rules.js.map +1 -1
- package/dist/index.d.ts +24 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -3
- package/dist/index.js.map +1 -1
- package/dist/io.d.ts +21 -0
- package/dist/io.d.ts.map +1 -0
- package/dist/io.js +59 -0
- package/dist/io.js.map +1 -0
- package/dist/money.d.ts +42 -0
- package/dist/money.d.ts.map +1 -0
- package/dist/money.js +112 -0
- package/dist/money.js.map +1 -0
- package/dist/program.d.ts +5 -24
- package/dist/program.d.ts.map +1 -1
- package/dist/program.js +25 -113
- package/dist/program.js.map +1 -1
- package/package.json +7 -8
- package/dist/auth/api-client.d.ts +0 -50
- package/dist/auth/api-client.d.ts.map +0 -1
- package/dist/auth/api-client.js +0 -109
- package/dist/auth/api-client.js.map +0 -1
- package/dist/auth/relative-time.d.ts +0 -2
- package/dist/auth/relative-time.d.ts.map +0 -1
- package/dist/auth/relative-time.js +0 -27
- package/dist/auth/relative-time.js.map +0 -1
- package/dist/auth/scopes.d.ts +0 -30
- package/dist/auth/scopes.d.ts.map +0 -1
- package/dist/auth/scopes.js +0 -39
- package/dist/auth/scopes.js.map +0 -1
- package/dist/auth/workspace-client.d.ts +0 -70
- package/dist/auth/workspace-client.d.ts.map +0 -1
- package/dist/auth/workspace-client.js +0 -105
- package/dist/auth/workspace-client.js.map +0 -1
- package/dist/commands/auth/config.command.d.ts +0 -8
- package/dist/commands/auth/config.command.d.ts.map +0 -1
- package/dist/commands/auth/config.command.js +0 -60
- package/dist/commands/auth/config.command.js.map +0 -1
- package/dist/commands/auth/login.command.d.ts +0 -46
- package/dist/commands/auth/login.command.d.ts.map +0 -1
- package/dist/commands/auth/login.command.js +0 -166
- package/dist/commands/auth/login.command.js.map +0 -1
- package/dist/commands/auth/logout.command.d.ts +0 -21
- package/dist/commands/auth/logout.command.d.ts.map +0 -1
- package/dist/commands/auth/logout.command.js +0 -97
- package/dist/commands/auth/logout.command.js.map +0 -1
- package/dist/commands/auth/methods.command.d.ts +0 -68
- package/dist/commands/auth/methods.command.d.ts.map +0 -1
- package/dist/commands/auth/methods.command.js +0 -91
- package/dist/commands/auth/methods.command.js.map +0 -1
- package/dist/commands/auth/status.command.d.ts +0 -18
- package/dist/commands/auth/status.command.d.ts.map +0 -1
- package/dist/commands/auth/status.command.js +0 -104
- package/dist/commands/auth/status.command.js.map +0 -1
- package/dist/commands/auth/use.command.d.ts +0 -17
- package/dist/commands/auth/use.command.d.ts.map +0 -1
- package/dist/commands/auth/use.command.js +0 -47
- package/dist/commands/auth/use.command.js.map +0 -1
- package/dist/commands/branding.command.d.ts +0 -3
- package/dist/commands/branding.command.d.ts.map +0 -1
- package/dist/commands/branding.command.js +0 -42
- package/dist/commands/branding.command.js.map +0 -1
- package/dist/commands/event.command.d.ts +0 -3
- package/dist/commands/event.command.d.ts.map +0 -1
- package/dist/commands/event.command.js +0 -27
- package/dist/commands/event.command.js.map +0 -1
- package/dist/commands/flag-init.command.d.ts +0 -13
- package/dist/commands/flag-init.command.d.ts.map +0 -1
- package/dist/commands/flag-init.command.js +0 -463
- package/dist/commands/flag-init.command.js.map +0 -1
- package/dist/commands/guide.command.d.ts +0 -61
- package/dist/commands/guide.command.d.ts.map +0 -1
- package/dist/commands/guide.command.js +0 -655
- package/dist/commands/guide.command.js.map +0 -1
- package/dist/commands/info.command.d.ts +0 -3
- package/dist/commands/info.command.d.ts.map +0 -1
- package/dist/commands/info.command.js +0 -70
- package/dist/commands/info.command.js.map +0 -1
- package/dist/commands/integration.command.d.ts +0 -35
- package/dist/commands/integration.command.d.ts.map +0 -1
- package/dist/commands/integration.command.js +0 -290
- package/dist/commands/integration.command.js.map +0 -1
- package/dist/commands/journeys.d.ts +0 -43
- package/dist/commands/journeys.d.ts.map +0 -1
- package/dist/commands/journeys.js +0 -221
- package/dist/commands/journeys.js.map +0 -1
- package/dist/commands/ops.command.d.ts +0 -17
- package/dist/commands/ops.command.d.ts.map +0 -1
- package/dist/commands/ops.command.js +0 -129
- package/dist/commands/ops.command.js.map +0 -1
- package/dist/commands/privilege.command.d.ts +0 -16
- package/dist/commands/privilege.command.d.ts.map +0 -1
- package/dist/commands/privilege.command.js +0 -86
- package/dist/commands/privilege.command.js.map +0 -1
- package/dist/commands/role.command.d.ts +0 -3
- package/dist/commands/role.command.d.ts.map +0 -1
- package/dist/commands/role.command.js +0 -101
- package/dist/commands/role.command.js.map +0 -1
- package/dist/commands/runtime-dir.d.ts +0 -16
- package/dist/commands/runtime-dir.d.ts.map +0 -1
- package/dist/commands/runtime-dir.js +0 -18
- package/dist/commands/runtime-dir.js.map +0 -1
- package/dist/commands/setup.command.d.ts +0 -63
- package/dist/commands/setup.command.d.ts.map +0 -1
- package/dist/commands/setup.command.js +0 -148
- package/dist/commands/setup.command.js.map +0 -1
- package/dist/commands/stripe.command.d.ts +0 -29
- package/dist/commands/stripe.command.d.ts.map +0 -1
- package/dist/commands/stripe.command.js +0 -99
- package/dist/commands/stripe.command.js.map +0 -1
- package/dist/commands/tenant.command.d.ts +0 -3
- package/dist/commands/tenant.command.d.ts.map +0 -1
- package/dist/commands/tenant.command.js +0 -135
- package/dist/commands/tenant.command.js.map +0 -1
- package/dist/commands/token.command.d.ts +0 -3
- package/dist/commands/token.command.d.ts.map +0 -1
- package/dist/commands/token.command.js +0 -54
- package/dist/commands/token.command.js.map +0 -1
- package/dist/commands/user.command.d.ts +0 -3
- package/dist/commands/user.command.d.ts.map +0 -1
- package/dist/commands/user.command.js +0 -117
- package/dist/commands/user.command.js.map +0 -1
- package/dist/credentials.d.ts +0 -141
- package/dist/credentials.d.ts.map +0 -1
- package/dist/credentials.js +0 -348
- package/dist/credentials.js.map +0 -1
- package/dist/flag-coherence.d.ts +0 -42
- package/dist/flag-coherence.d.ts.map +0 -1
- package/dist/flag-coherence.js +0 -103
- package/dist/flag-coherence.js.map +0 -1
- package/dist/output.d.ts +0 -17
- package/dist/output.d.ts.map +0 -1
- package/dist/output.js +0 -107
- package/dist/output.js.map +0 -1
- package/dist/preview.d.ts +0 -9
- package/dist/preview.d.ts.map +0 -1
- package/dist/preview.js +0 -63
- package/dist/preview.js.map +0 -1
- package/dist/product-decisions.d.ts +0 -40
- package/dist/product-decisions.d.ts.map +0 -1
- package/dist/product-decisions.js +0 -52
- package/dist/product-decisions.js.map +0 -1
- package/dist/prompt-urls.d.ts +0 -31
- package/dist/prompt-urls.d.ts.map +0 -1
- package/dist/prompt-urls.js +0 -35
- package/dist/prompt-urls.js.map +0 -1
- package/dist/prompts/auth-master-integration-prompt.md +0 -255
- package/dist/prompts/billing/master.md +0 -332
- package/dist/prompts/decisions/feature-control.md +0 -131
- package/dist/prompts/decisions/going-live.md +0 -60
- package/dist/prompts/decisions/login.md +0 -71
- package/dist/prompts/decisions/look-and-feel.md +0 -68
- package/dist/prompts/decisions/payments.md +0 -97
- package/dist/prompts/decisions/roles.md +0 -78
- package/dist/prompts/decisions/teams.md +0 -61
- package/dist/prompts/fit-together.md +0 -97
- package/dist/prompts/flags/master.md +0 -220
- package/dist/prompts/guide-coverage.json +0 -17
- package/dist/prompts/integration-success.md +0 -106
- package/dist/prompts/mcp-tools.json +0 -90
- package/dist/prompts/mechanisms.md +0 -304
- package/dist/prompts/orientation.md +0 -42
- package/dist/prompts/product-decisions.json +0 -338
- package/dist/resolve.d.ts +0 -110
- package/dist/resolve.d.ts.map +0 -1
- package/dist/resolve.js +0 -260
- package/dist/resolve.js.map +0 -1
- package/dist/shape.d.ts +0 -25
- package/dist/shape.d.ts.map +0 -1
- package/dist/shape.js +0 -74
- package/dist/shape.js.map +0 -1
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { CliError } from '../core/errors.js';
|
|
2
|
+
export const FLAG_STATES = ['off', 'on', 'on-with-rule'];
|
|
3
|
+
export const VALUE_TYPES = ['boolean', 'string', 'number', 'json'];
|
|
4
|
+
export const OPERATORS = ['eq', 'neq', 'contains', 'not_contains', 'in', 'not_in', 'gt', 'lt', 'between', 'regex', 'exists', 'not_exists'];
|
|
5
|
+
const body = (answer) => answer.body;
|
|
6
|
+
/** `If-Match` for a change made on the flag as it was read: version 2 refuses a change that names no revision. */
|
|
7
|
+
const revisionOf = (flag) => ({ 'if-match': `"${String(flag.revision)}"` });
|
|
8
|
+
export const flags = {
|
|
9
|
+
list: (c, appId) => c.person('flagsList', { params: { appId } }).then(body),
|
|
10
|
+
get: (c, appId, key) => c.person('flagsGet', { params: { appId, key } }).then(body),
|
|
11
|
+
create: (c, appId, fields) => c.person('flagsCreate', { params: { appId }, body: fields }).then(body),
|
|
12
|
+
/** Changes the flag as it is now: read, changed, written with its revision, so a change made meanwhile is never overwritten. */
|
|
13
|
+
async update(c, appId, key, change) {
|
|
14
|
+
const current = await flags.get(c, appId, key);
|
|
15
|
+
const fields = typeof change === 'function' ? change(current) : change;
|
|
16
|
+
return c.person('flagsUpdate', { params: { appId, key }, body: fields, headers: revisionOf(current) }).then(body);
|
|
17
|
+
},
|
|
18
|
+
async remove(c, appId, key) {
|
|
19
|
+
const current = await flags.get(c, appId, key);
|
|
20
|
+
return c.person('flagsDelete', { params: { appId, key }, headers: revisionOf(current) }).then(body);
|
|
21
|
+
},
|
|
22
|
+
/** Switches the flag off and keeps its rule, so `restore` brings back what it was. */
|
|
23
|
+
kill: (c, appId, key) => c.person('flagsKill', { params: { appId, key } }).then(body),
|
|
24
|
+
restore: (c, appId, key) => c.person('flagsRestore', { params: { appId, key } }).then(body),
|
|
25
|
+
segments: (c, appId) => c.person('segmentsList', { params: { appId } }).then(body),
|
|
26
|
+
};
|
|
27
|
+
/** A value written in a condition: true, false, null and numbers as themselves, anything else as text. */
|
|
28
|
+
function conditionValue(raw) {
|
|
29
|
+
if (raw === 'true')
|
|
30
|
+
return true;
|
|
31
|
+
if (raw === 'false')
|
|
32
|
+
return false;
|
|
33
|
+
if (raw === 'null')
|
|
34
|
+
return null;
|
|
35
|
+
if (/^-?\d+(\.\d+)?$/.test(raw))
|
|
36
|
+
return Number(raw);
|
|
37
|
+
return raw.replace(/^"(.*)"$/, '$1');
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* One condition written in words: `<attribute> <operator> [value[,value…]]`.
|
|
41
|
+
* `in`, `not_in` and `between` take a comma-separated list; `exists` and
|
|
42
|
+
* `not_exists` take none.
|
|
43
|
+
*
|
|
44
|
+
* privileges contains USER_WRITE
|
|
45
|
+
* bridge:billing.entitlement.analytics eq true
|
|
46
|
+
* user.email in a@example.com,b@example.com
|
|
47
|
+
*/
|
|
48
|
+
export function parseCondition(text) {
|
|
49
|
+
const match = /^\s*(\S+)\s+(\S+)(?:\s+(.*?))?\s*$/.exec(text);
|
|
50
|
+
if (!match)
|
|
51
|
+
throw new CliError('invalid', `A condition is "<attribute> <operator> <value>": ${text}`);
|
|
52
|
+
const [, attribute, operator, rest] = match;
|
|
53
|
+
if (!OPERATORS.includes(operator)) {
|
|
54
|
+
throw new CliError('invalid', `${operator} is not an operator. The operators are: ${OPERATORS.join(', ')}.`);
|
|
55
|
+
}
|
|
56
|
+
const op = operator;
|
|
57
|
+
if (op === 'exists' || op === 'not_exists') {
|
|
58
|
+
if (rest)
|
|
59
|
+
throw new CliError('invalid', `${op} takes no value: ${text}`);
|
|
60
|
+
return { attribute, operator: op, values: [] };
|
|
61
|
+
}
|
|
62
|
+
if (rest === undefined || rest === '')
|
|
63
|
+
throw new CliError('invalid', `${op} needs a value: ${text}`);
|
|
64
|
+
const values = op === 'in' || op === 'not_in' || op === 'between' ? rest.split(',').map((each) => conditionValue(each.trim())) : [conditionValue(rest)];
|
|
65
|
+
if (op === 'between' && values.length !== 2)
|
|
66
|
+
throw new CliError('invalid', `between takes two values, "low,high": ${text}`);
|
|
67
|
+
return { attribute, operator: op, values };
|
|
68
|
+
}
|
|
69
|
+
/** A flag value written on the command line, read as the flag's type. */
|
|
70
|
+
export function parseValue(raw, type) {
|
|
71
|
+
switch (type) {
|
|
72
|
+
case 'boolean':
|
|
73
|
+
if (raw !== 'true' && raw !== 'false')
|
|
74
|
+
throw new CliError('invalid', `A boolean flag's value is true or false, not ${raw}.`);
|
|
75
|
+
return raw === 'true';
|
|
76
|
+
case 'number': {
|
|
77
|
+
const n = Number(raw);
|
|
78
|
+
if (raw.trim() === '' || !Number.isFinite(n))
|
|
79
|
+
throw new CliError('invalid', `A number flag's value is a number, not ${raw}.`);
|
|
80
|
+
return n;
|
|
81
|
+
}
|
|
82
|
+
case 'json':
|
|
83
|
+
try {
|
|
84
|
+
return JSON.parse(raw);
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
throw new CliError('invalid', `A json flag's value is JSON: ${raw}`);
|
|
88
|
+
}
|
|
89
|
+
default:
|
|
90
|
+
return raw;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/** A rule given as JSON, checked for its shape before it is sent. */
|
|
94
|
+
export function parseRule(json) {
|
|
95
|
+
let parsed;
|
|
96
|
+
try {
|
|
97
|
+
parsed = JSON.parse(json);
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
throw new CliError('invalid', 'The rule is not valid JSON.');
|
|
101
|
+
}
|
|
102
|
+
const rule = parsed;
|
|
103
|
+
if (typeof rule !== 'object' || rule === null || !Array.isArray(rule.branches) || !('otherwiseValue' in rule)) {
|
|
104
|
+
throw new CliError('invalid', 'A rule is { "branches": [{ "conditions": [...], "returnValue": … }], "otherwiseValue": … }.');
|
|
105
|
+
}
|
|
106
|
+
return rule;
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=flags.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flags.js","sourceRoot":"","sources":["../../src/api/flags.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { BridgeClient } from '../client.js';
|
|
2
|
+
export interface Key {
|
|
3
|
+
readonly id: string;
|
|
4
|
+
readonly name: string;
|
|
5
|
+
readonly kind?: string;
|
|
6
|
+
readonly privileges: string[];
|
|
7
|
+
readonly expiresAt: string;
|
|
8
|
+
readonly revoked?: boolean;
|
|
9
|
+
readonly [field: string]: unknown;
|
|
10
|
+
}
|
|
11
|
+
/** A key just made: the secret is in `key`, shown this once. */
|
|
12
|
+
export interface MadeKey extends Key {
|
|
13
|
+
readonly key: string;
|
|
14
|
+
}
|
|
15
|
+
export interface KeyFields {
|
|
16
|
+
readonly name: string;
|
|
17
|
+
readonly privileges?: string[];
|
|
18
|
+
readonly expiresInDays?: number;
|
|
19
|
+
}
|
|
20
|
+
export declare const keys: {
|
|
21
|
+
list(c: BridgeClient, appId: string): Promise<Key[]>;
|
|
22
|
+
create(c: BridgeClient, appId: string, fields: KeyFields): Promise<MadeKey>;
|
|
23
|
+
revoke(c: BridgeClient, appId: string, keyId: string): Promise<void>;
|
|
24
|
+
};
|
|
25
|
+
/** The app an app key belongs to: keys read `bk.<app>.<id>.<secret>`. */
|
|
26
|
+
export declare function appOfKey(key: string): string | null;
|
|
27
|
+
/** What `withAppKey` reports with and listens to. Real ones by default; tests pass their own. */
|
|
28
|
+
export interface AppKeyOptions {
|
|
29
|
+
readonly warn?: (message: string) => void;
|
|
30
|
+
/** Where SIGINT and SIGTERM arrive. */
|
|
31
|
+
readonly signals?: Pick<NodeJS.EventEmitter, 'on' | 'off'>;
|
|
32
|
+
readonly exit?: (code: number) => void;
|
|
33
|
+
/** How long a revoke may take when the command is stopped, in ms. */
|
|
34
|
+
readonly revokeTimeoutMs?: number;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Runs `fn` with an app key of the app, for the routes version 2 offers
|
|
38
|
+
* only to the app's own server.
|
|
39
|
+
*
|
|
40
|
+
* The key is `BRIDGE_APP_KEY` when it is set (it must be this app's).
|
|
41
|
+
* Otherwise the CLI makes a key that lasts one day and holds only the
|
|
42
|
+
* privileges this command needs, and revokes it once `fn` is done, when it
|
|
43
|
+
* fails, and when the command is stopped (Ctrl-C, SIGTERM). If it cannot be
|
|
44
|
+
* revoked, the CLI says which key to revoke (its id, never its secret).
|
|
45
|
+
* Making a key needs a recent sign-in.
|
|
46
|
+
*/
|
|
47
|
+
export declare function withAppKey<T>(c: BridgeClient, appId: string, privileges: string[], env: NodeJS.ProcessEnv, fn: (key: string) => Promise<T>, options?: AppKeyOptions): Promise<T>;
|
|
48
|
+
//# sourceMappingURL=keys.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../../src/api/keys.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAGjD,MAAM,WAAW,GAAG;IAClB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B,QAAQ,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;CACnC;AAED,gEAAgE;AAChE,MAAM,WAAW,OAAQ,SAAQ,GAAG;IAClC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IAC/B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;CACjC;AAED,eAAO,MAAM,IAAI;YACD,YAAY,SAAS,MAAM,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;cAG1C,YAAY,SAAS,MAAM,UAAU,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC;cAGjE,YAAY,SAAS,MAAM,SAAS,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAG3E,CAAC;AAEF,yEAAyE;AACzE,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGnD;AAED,iGAAiG;AACjG,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,uCAAuC;IACvC,QAAQ,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,IAAI,GAAG,KAAK,CAAC,CAAC;IAC3D,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,qEAAqE;IACrE,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;CACnC;AAID;;;;;;;;;;GAUG;AACH,wBAAsB,UAAU,CAAC,CAAC,EAChC,CAAC,EAAE,YAAY,EACf,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,EAAE,EACpB,GAAG,EAAE,MAAM,CAAC,UAAU,EACtB,EAAE,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,EAC/B,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,CAAC,CAAC,CAuDZ"}
|
package/dist/api/keys.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { CliError } from '../core/errors.js';
|
|
2
|
+
export const keys = {
|
|
3
|
+
async list(c, appId) {
|
|
4
|
+
return (await c.person('keysList', { params: { appId } })).body;
|
|
5
|
+
},
|
|
6
|
+
async create(c, appId, fields) {
|
|
7
|
+
return (await c.person('keysCreate', { params: { appId }, body: fields })).body;
|
|
8
|
+
},
|
|
9
|
+
async revoke(c, appId, keyId) {
|
|
10
|
+
await c.person('keysRevoke', { params: { appId, keyId } });
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
/** The app an app key belongs to: keys read `bk.<app>.<id>.<secret>`. */
|
|
14
|
+
export function appOfKey(key) {
|
|
15
|
+
const parts = key.split('.');
|
|
16
|
+
return parts.length === 4 && parts[0] === 'bk' && parts[1] !== '' ? parts[1] : null;
|
|
17
|
+
}
|
|
18
|
+
const SIGNALS = { SIGINT: 130, SIGTERM: 143 };
|
|
19
|
+
/**
|
|
20
|
+
* Runs `fn` with an app key of the app, for the routes version 2 offers
|
|
21
|
+
* only to the app's own server.
|
|
22
|
+
*
|
|
23
|
+
* The key is `BRIDGE_APP_KEY` when it is set (it must be this app's).
|
|
24
|
+
* Otherwise the CLI makes a key that lasts one day and holds only the
|
|
25
|
+
* privileges this command needs, and revokes it once `fn` is done, when it
|
|
26
|
+
* fails, and when the command is stopped (Ctrl-C, SIGTERM). If it cannot be
|
|
27
|
+
* revoked, the CLI says which key to revoke (its id, never its secret).
|
|
28
|
+
* Making a key needs a recent sign-in.
|
|
29
|
+
*/
|
|
30
|
+
export async function withAppKey(c, appId, privileges, env, fn, options = {}) {
|
|
31
|
+
const warn = options.warn ?? ((message) => process.stderr.write(`${message}\n`));
|
|
32
|
+
const signals = options.signals ?? process;
|
|
33
|
+
const exit = options.exit ?? ((code) => process.exit(code));
|
|
34
|
+
const given = env['BRIDGE_APP_KEY']?.trim();
|
|
35
|
+
if (given) {
|
|
36
|
+
const owner = appOfKey(given);
|
|
37
|
+
if (owner !== null && owner !== appId) {
|
|
38
|
+
throw new CliError('misconfigured', `BRIDGE_APP_KEY belongs to app ${owner}, not to ${appId}.`);
|
|
39
|
+
}
|
|
40
|
+
return fn(given);
|
|
41
|
+
}
|
|
42
|
+
let made;
|
|
43
|
+
try {
|
|
44
|
+
made = await keys.create(c, appId, { name: 'bridge-cli (temporary)', privileges, expiresInDays: 1 });
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
if (error instanceof CliError && error.kind === 'reauthenticate') {
|
|
48
|
+
throw new CliError('reauthenticate', 'This command makes a short-lived app key, which needs a recent sign-in.', {
|
|
49
|
+
status: error.status,
|
|
50
|
+
hint: 'Sign in again with `bridge auth login` and run it within 5 minutes, or set BRIDGE_APP_KEY to one of the app\'s keys.',
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
throw error;
|
|
54
|
+
}
|
|
55
|
+
let revoking;
|
|
56
|
+
const revoke = (timeoutMs) => {
|
|
57
|
+
revoking ??= (async () => {
|
|
58
|
+
const attempt = keys.revoke(c, appId, made.id);
|
|
59
|
+
const limited = timeoutMs === undefined
|
|
60
|
+
? attempt
|
|
61
|
+
: Promise.race([attempt, new Promise((_, reject) => setTimeout(() => reject(new Error(`no answer within ${String(timeoutMs / 1000)} s`)), timeoutMs).unref?.())]);
|
|
62
|
+
try {
|
|
63
|
+
await limited;
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
67
|
+
warn(`The temporary key ${made.id} could not be revoked (${reason}); it expires within a day. Revoke it with \`bridge key revoke ${made.id}\`.`);
|
|
68
|
+
}
|
|
69
|
+
})();
|
|
70
|
+
return revoking;
|
|
71
|
+
};
|
|
72
|
+
const handlers = Object.entries(SIGNALS).map(([signal, code]) => {
|
|
73
|
+
const handler = () => {
|
|
74
|
+
void revoke(options.revokeTimeoutMs ?? 5000).then(() => exit(code));
|
|
75
|
+
};
|
|
76
|
+
signals.on(signal, handler);
|
|
77
|
+
return () => signals.off(signal, handler);
|
|
78
|
+
});
|
|
79
|
+
try {
|
|
80
|
+
return await fn(made.key);
|
|
81
|
+
}
|
|
82
|
+
finally {
|
|
83
|
+
await revoke();
|
|
84
|
+
handlers.forEach((stop) => stop());
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=keys.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keys.js","sourceRoot":"","sources":["../../src/api/keys.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,10 +3,27 @@ export interface OpenBrowserResult {
|
|
|
3
3
|
/** Short reason the launch failed (only set when `ok === false`). */
|
|
4
4
|
reason?: string;
|
|
5
5
|
}
|
|
6
|
+
/** The program and the argument array that open `url` on `platform`. Exposed for tests. */
|
|
7
|
+
export declare function browserCommand(url: string, platform: NodeJS.Platform): {
|
|
8
|
+
command: string;
|
|
9
|
+
args: string[];
|
|
10
|
+
};
|
|
11
|
+
type ExecFile = (file: string, args: readonly string[], options: {
|
|
12
|
+
windowsHide: boolean;
|
|
13
|
+
shell: false;
|
|
14
|
+
}, callback: (error: Error | null) => void) => {
|
|
15
|
+
unref?: () => void;
|
|
16
|
+
on?: (event: 'error', listener: (error: Error) => void) => unknown;
|
|
17
|
+
} | undefined;
|
|
18
|
+
export interface OpenBrowserDeps {
|
|
19
|
+
readonly platform?: NodeJS.Platform;
|
|
20
|
+
readonly execFile?: ExecFile;
|
|
21
|
+
}
|
|
6
22
|
/**
|
|
7
|
-
* Attempts to open `url` in the
|
|
8
|
-
* `{ ok: true }` on success, `{ ok: false, reason }` on failure (caller
|
|
9
|
-
*
|
|
23
|
+
* Attempts to open `url` in the person's default browser. Resolves to
|
|
24
|
+
* `{ ok: true }` on success, `{ ok: false, reason }` on failure (the caller
|
|
25
|
+
* then prints the address to open by hand).
|
|
10
26
|
*/
|
|
11
|
-
export declare function openBrowser(url: string): Promise<OpenBrowserResult>;
|
|
27
|
+
export declare function openBrowser(url: string, deps?: OpenBrowserDeps): Promise<OpenBrowserResult>;
|
|
28
|
+
export {};
|
|
12
29
|
//# sourceMappingURL=browser-open.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browser-open.d.ts","sourceRoot":"","sources":["../../src/auth/browser-open.ts"],"names":[],"mappings":"AAiBA,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,OAAO,CAAC;IACZ,qEAAqE;IACrE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,wBAAsB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,
|
|
1
|
+
{"version":3,"file":"browser-open.d.ts","sourceRoot":"","sources":["../../src/auth/browser-open.ts"],"names":[],"mappings":"AAiBA,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,OAAO,CAAC;IACZ,qEAAqE;IACrE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,2FAA2F;AAC3F,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,CAI1G;AAED,KAAK,QAAQ,GAAG,CACd,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,OAAO,EAAE;IAAE,WAAW,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,KAAK,CAAA;CAAE,EAC/C,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,KAAK,IAAI,KACpC;IAAE,KAAK,CAAC,EAAE,MAAM,IAAI,CAAC;IAAC,EAAE,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,KAAK,OAAO,CAAA;CAAE,GAAG,SAAS,CAAC;AAE5G,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;IACpC,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC;CAC9B;AAED;;;;GAIG;AACH,wBAAsB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,GAAE,eAAoB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA6BrG"}
|
|
@@ -1,81 +1,61 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Opens an address in the default browser, with no shell anywhere.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Each platform's opener gets the address as one argument of an argument
|
|
5
|
+
* array, through `execFile`, which never starts a shell:
|
|
5
6
|
* - macOS: `open <url>`
|
|
6
|
-
* - Windows: `
|
|
7
|
-
*
|
|
8
|
-
* - Linux:
|
|
7
|
+
* - Windows: `rundll32 url.dll,FileProtocolHandler <url>`. Never `cmd /c start`:
|
|
8
|
+
* cmd cuts the address at `&` and runs what follows as commands.
|
|
9
|
+
* - Linux and the rest: `xdg-open <url>`
|
|
9
10
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* Implemented directly on `child_process` to avoid a heavyweight dependency
|
|
14
|
-
* for what is ~30 lines of platform detection.
|
|
11
|
+
* Only addresses the CLI would call itself are opened: https, or http on
|
|
12
|
+
* this machine. Anything else (`file:`, `javascript:`, a plain-http host) is
|
|
13
|
+
* refused before any program runs.
|
|
15
14
|
*/
|
|
16
|
-
import {
|
|
15
|
+
import { execFile } from 'node:child_process';
|
|
16
|
+
import { secureUrl } from '../core/urls.js';
|
|
17
|
+
/** The program and the argument array that open `url` on `platform`. Exposed for tests. */
|
|
18
|
+
export function browserCommand(url, platform) {
|
|
19
|
+
if (platform === 'darwin')
|
|
20
|
+
return { command: 'open', args: [url] };
|
|
21
|
+
if (platform === 'win32')
|
|
22
|
+
return { command: 'rundll32', args: ['url.dll,FileProtocolHandler', url] };
|
|
23
|
+
return { command: 'xdg-open', args: [url] };
|
|
24
|
+
}
|
|
17
25
|
/**
|
|
18
|
-
* Attempts to open `url` in the
|
|
19
|
-
* `{ ok: true }` on success, `{ ok: false, reason }` on failure (caller
|
|
20
|
-
*
|
|
26
|
+
* Attempts to open `url` in the person's default browser. Resolves to
|
|
27
|
+
* `{ ok: true }` on success, `{ ok: false, reason }` on failure (the caller
|
|
28
|
+
* then prints the address to open by hand).
|
|
21
29
|
*/
|
|
22
|
-
export async function openBrowser(url) {
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
args = [url];
|
|
29
|
-
}
|
|
30
|
-
else if (platform === 'win32') {
|
|
31
|
-
// `cmd /c start "" <url>` — empty quoted string is the (ignored) window title.
|
|
32
|
-
command = 'cmd';
|
|
33
|
-
args = ['/c', 'start', '""', url];
|
|
34
|
-
}
|
|
35
|
-
else {
|
|
36
|
-
// Treat anything else (linux, freebsd, etc.) as xdg-open.
|
|
37
|
-
command = 'xdg-open';
|
|
38
|
-
args = [url];
|
|
39
|
-
}
|
|
30
|
+
export async function openBrowser(url, deps = {}) {
|
|
31
|
+
const parsed = secureUrl(url);
|
|
32
|
+
if (parsed === null)
|
|
33
|
+
return { ok: false, reason: 'only https addresses (http on this machine) are opened' };
|
|
34
|
+
const { command, args } = browserCommand(parsed.toString(), deps.platform ?? process.platform);
|
|
35
|
+
const run = deps.execFile ?? execFile;
|
|
40
36
|
return new Promise((resolve) => {
|
|
41
|
-
let child;
|
|
42
|
-
try {
|
|
43
|
-
child = spawn(command, args, {
|
|
44
|
-
stdio: 'ignore',
|
|
45
|
-
// detached so the browser process lives independently of the CLI.
|
|
46
|
-
// unref() so the CLI can exit without waiting for the browser to close.
|
|
47
|
-
detached: true,
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
catch (err) {
|
|
51
|
-
resolve({ ok: false, reason: err instanceof Error ? err.message : String(err) });
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
37
|
let settled = false;
|
|
55
|
-
const settle = (
|
|
38
|
+
const settle = (result) => {
|
|
56
39
|
if (settled)
|
|
57
40
|
return;
|
|
58
41
|
settled = true;
|
|
59
|
-
resolve(
|
|
42
|
+
resolve(result);
|
|
60
43
|
};
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
//
|
|
75
|
-
//
|
|
76
|
-
// browser opened but the parent process hung. The user only sees a
|
|
77
|
-
// problem if no callback ever arrives, and the loopback timeout handles
|
|
78
|
-
// that case.
|
|
44
|
+
try {
|
|
45
|
+
const child = run(command, args, { windowsHide: true, shell: false }, (error) => {
|
|
46
|
+
settle(error ? { ok: false, reason: error.message } : { ok: true });
|
|
47
|
+
});
|
|
48
|
+
child?.on?.('error', (error) => settle({ ok: false, reason: error.message }));
|
|
49
|
+
// Don't keep the CLI alive for the opener.
|
|
50
|
+
child?.unref?.();
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
settle({ ok: false, reason: error instanceof Error ? error.message : String(error) });
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
// Most openers return within a fraction of a second. If this one has not
|
|
57
|
+
// answered in 2 s, assume the browser opened; the loopback's timeout
|
|
58
|
+
// covers the case where no callback ever arrives.
|
|
79
59
|
setTimeout(() => settle({ ok: true }), 2000).unref?.();
|
|
80
60
|
});
|
|
81
61
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browser-open.js","sourceRoot":"","sources":["../../src/auth/browser-open.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"browser-open.js","sourceRoot":"","sources":["../../src/auth/browser-open.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAQ5C,2FAA2F;AAC3F,MAAM,UAAU,cAAc,CAAC,GAAW,EAAE,QAAyB;IACnE,IAAI,QAAQ,KAAK,QAAQ;QAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC;IACnE,IAAI,QAAQ,KAAK,OAAO;QAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,6BAA6B,EAAE,GAAG,CAAC,EAAE,CAAC;IACrG,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC;AAC9C,CAAC;AAcD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,GAAW,EAAE,OAAwB,EAAE;IACvE,MAAM,MAAM,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;IAC9B,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,wDAAwD,EAAE,CAAC;IAC5G,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,cAAc,CAAC,MAAM,CAAC,QAAQ,EAAE,EAAE,IAAI,CAAC,QAAQ,IAAI,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC/F,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,IAAK,QAAgC,CAAC;IAE/D,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,MAAM,MAAM,GAAG,CAAC,MAAyB,EAAE,EAAE;YAC3C,IAAI,OAAO;gBAAE,OAAO;YACpB,OAAO,GAAG,IAAI,CAAC;YACf,OAAO,CAAC,MAAM,CAAC,CAAC;QAClB,CAAC,CAAC;QACF,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,EAAE,EAAE;gBAC9E,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;YACtE,CAAC,CAAC,CAAC;YACH,KAAK,EAAE,EAAE,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;YAC9E,2CAA2C;YAC3C,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC;QACnB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YACtF,OAAO;QACT,CAAC;QACD,yEAAyE;QACzE,qEAAqE;QACrE,kDAAkD;QAClD,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC;IACzD,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runs `fn` while holding a lock file, so two commands running at once never
|
|
3
|
+
* renew with the same refresh token. Bridge rotates refresh tokens and ends
|
|
4
|
+
* the session when one is used twice; this keeps that from happening by
|
|
5
|
+
* accident. A lock left by a command that crashed is taken over after 30 s.
|
|
6
|
+
*/
|
|
7
|
+
export declare function withLock<T>(dir: string, name: string, fn: () => Promise<T>): Promise<T>;
|
|
8
|
+
//# sourceMappingURL=lock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lock.d.ts","sourceRoot":"","sources":["../../src/auth/lock.ts"],"names":[],"mappings":"AASA;;;;;GAKG;AACH,wBAAsB,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAyB7F"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { lstat, open, unlink } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { ensurePrivateDir } from '../core/private-files.js';
|
|
4
|
+
const STALE_MS = 30_000;
|
|
5
|
+
const WAIT_MS = 15_000;
|
|
6
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
7
|
+
/**
|
|
8
|
+
* Runs `fn` while holding a lock file, so two commands running at once never
|
|
9
|
+
* renew with the same refresh token. Bridge rotates refresh tokens and ends
|
|
10
|
+
* the session when one is used twice; this keeps that from happening by
|
|
11
|
+
* accident. A lock left by a command that crashed is taken over after 30 s.
|
|
12
|
+
*/
|
|
13
|
+
export async function withLock(dir, name, fn) {
|
|
14
|
+
await ensurePrivateDir(dir);
|
|
15
|
+
const path = join(dir, `${name}.lock`);
|
|
16
|
+
const deadline = Date.now() + WAIT_MS;
|
|
17
|
+
for (;;) {
|
|
18
|
+
try {
|
|
19
|
+
const handle = await open(path, 'wx', 0o600);
|
|
20
|
+
await handle.close();
|
|
21
|
+
break;
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
if (error.code !== 'EEXIST')
|
|
25
|
+
throw error;
|
|
26
|
+
const age = await lstat(path).then((info) => Date.now() - info.mtimeMs, () => 0);
|
|
27
|
+
if (age > STALE_MS) {
|
|
28
|
+
await unlink(path).catch(() => undefined);
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
if (Date.now() > deadline)
|
|
32
|
+
throw new Error(`Another bridge command holds ${path}. If none is running, delete that file.`);
|
|
33
|
+
await sleep(100);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
return await fn();
|
|
38
|
+
}
|
|
39
|
+
finally {
|
|
40
|
+
await unlink(path).catch(() => undefined);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=lock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lock.js","sourceRoot":"","sources":["../../src/auth/lock.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAE5D,MAAM,QAAQ,GAAG,MAAM,CAAC;AACxB,MAAM,OAAO,GAAG,MAAM,CAAC;AAEvB,MAAM,KAAK,GAAG,CAAC,EAAU,EAAE,EAAE,CAAC,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAEhF;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAI,GAAW,EAAE,IAAY,EAAE,EAAoB;IAC/E,MAAM,gBAAgB,CAAC,GAAG,CAAC,CAAC;IAC5B,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,OAAO,CAAC,CAAC;IACvC,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC;IACtC,SAAS,CAAC;QACR,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;YAC7C,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;YACrB,MAAM;QACR,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;gBAAE,MAAM,KAAK,CAAC;YACpE,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;YACjF,IAAI,GAAG,GAAG,QAAQ,EAAE,CAAC;gBACnB,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;gBAC1C,SAAS;YACX,CAAC;YACD,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ;gBAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,IAAI,yCAAyC,CAAC,CAAC;YAC1H,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC;QACnB,CAAC;IACH,CAAC;IACD,IAAI,CAAC;QACH,OAAO,MAAM,EAAE,EAAE,CAAC;IACpB,CAAC;YAAS,CAAC;QACT,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IAC5C,CAAC;AACH,CAAC"}
|
package/dist/auth/loopback.d.ts
CHANGED
|
@@ -3,22 +3,17 @@ export interface LoopbackResult {
|
|
|
3
3
|
state: string;
|
|
4
4
|
}
|
|
5
5
|
export interface RunLoopbackOptions {
|
|
6
|
-
/** The CSRF state the caller generated; must match what the
|
|
6
|
+
/** The CSRF state the caller generated; must match what the page echoes. */
|
|
7
7
|
expectedState: string;
|
|
8
|
-
/**
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
* branded "you can close this tab" page on cloud-views.
|
|
12
|
-
* Falls back to inline HTML if not provided.
|
|
13
|
-
*/
|
|
14
|
-
successRedirectUrl?: string;
|
|
15
|
-
/** Body of the success page rendered to the browser when no redirect URL is set. */
|
|
8
|
+
/** The issuer: the callback must name it (`iss`, RFC 9207). */
|
|
9
|
+
expectedIssuer?: string;
|
|
10
|
+
/** Body of the success page rendered to the browser. */
|
|
16
11
|
successHtml?: string;
|
|
17
12
|
/** Total time (ms) we'll wait for the redirect before giving up. */
|
|
18
13
|
timeoutMs?: number;
|
|
19
14
|
}
|
|
20
15
|
export interface StartedLoopback {
|
|
21
|
-
/** The redirect URI to pass to the
|
|
16
|
+
/** The redirect URI to pass to the sign-in page, e.g. `http://127.0.0.1:54321/callback`. */
|
|
22
17
|
redirectUri: string;
|
|
23
18
|
/** Resolves with the verified `{ code, state }` once the redirect arrives. */
|
|
24
19
|
result: Promise<LoopbackResult>;
|
|
@@ -36,18 +31,19 @@ export declare function parseCallbackUrl(rawUrl: string): {
|
|
|
36
31
|
state?: string;
|
|
37
32
|
error?: string;
|
|
38
33
|
errorDescription?: string;
|
|
34
|
+
iss?: string;
|
|
39
35
|
pathname: string;
|
|
40
36
|
};
|
|
41
37
|
/**
|
|
42
38
|
* Validates a parsed callback against the expected CSRF state. Returns the
|
|
43
39
|
* verified `{code, state}` or throws a friendly error.
|
|
44
40
|
*/
|
|
45
|
-
export declare function verifyCallback(parsed: ReturnType<typeof parseCallbackUrl>, expectedState: string): LoopbackResult;
|
|
41
|
+
export declare function verifyCallback(parsed: ReturnType<typeof parseCallbackUrl>, expectedState: string, expectedIssuer?: string): LoopbackResult;
|
|
46
42
|
/**
|
|
47
43
|
* Boots the single-shot loopback listener on a random ephemeral port.
|
|
48
44
|
*
|
|
49
45
|
* Returns immediately with `{ redirectUri, result }`. The caller passes
|
|
50
|
-
* `redirectUri` to the
|
|
46
|
+
* `redirectUri` to the sign-in page, then awaits `result` for the verified `{code, state}`.
|
|
51
47
|
*
|
|
52
48
|
* The server:
|
|
53
49
|
* - Closes itself after the first GET to `/callback` completes its response.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loopback.d.ts","sourceRoot":"","sources":["../../src/auth/loopback.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"loopback.d.ts","sourceRoot":"","sources":["../../src/auth/loopback.ts"],"names":[],"mappings":"AAmBA,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,kBAAkB;IACjC,4EAA4E;IAC5E,aAAa,EAAE,MAAM,CAAC;IACtB,+DAA+D;IAC/D,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,wDAAwD;IACxD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oEAAoE;IACpE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B,4FAA4F;IAC5F,WAAW,EAAE,MAAM,CAAC;IACpB,8EAA8E;IAC9E,MAAM,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;IAChC,2EAA2E;IAC3E,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAmHD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG;IAChD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB,CAWA;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,UAAU,CAAC,OAAO,gBAAgB,CAAC,EAC3C,aAAa,EAAE,MAAM,EACrB,cAAc,CAAC,EAAE,MAAM,GACtB,cAAc,CA2BhB;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,eAAe,CAAC,CA6FtF;AAED,qBAAa,aAAc,SAAQ,KAAK;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBACV,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;CAK1C"}
|