@myapihq/cli 2.31.1 → 2.31.3
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/flags.d.ts +1 -0
- package/dist/flags.js +40 -3
- package/dist/flags.test.js +40 -1
- package/dist/index.js +2 -3
- package/package.json +2 -2
package/dist/flags.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export type FlagType = 'string' | 'boolean' | 'number' | 'list';
|
|
2
2
|
export type FlagSchema = Record<string, FlagType>;
|
|
3
3
|
export declare const GLOBAL_FLAGS: FlagSchema;
|
|
4
|
+
export declare const DISPATCH_FLAGS: ReadonlySet<string>;
|
|
4
5
|
export interface ParsedArgs {
|
|
5
6
|
args: string[];
|
|
6
7
|
flags: Record<string, string | boolean | number>;
|
package/dist/flags.js
CHANGED
|
@@ -5,13 +5,40 @@
|
|
|
5
5
|
// utils.ts — adding a new value flag without registering it would silently
|
|
6
6
|
// turn it into a boolean (e.g. `--html '<p>'` becoming `flags.html === true`).
|
|
7
7
|
// Flags every command understands.
|
|
8
|
+
//
|
|
9
|
+
// `org` belongs here rather than in each command's own schema. Ten commands
|
|
10
|
+
// declared it individually and the other nineteen did not, so the same flag
|
|
11
|
+
// refused a duplicate on `myapi pixel` and silently kept the last value on
|
|
12
|
+
// `myapi funnel push` — see the duplicate check in parseFlags for why last-wins
|
|
13
|
+
// is the one outcome you cannot recover from. For the tenant selector it picks
|
|
14
|
+
// the organisation your write lands in, which makes it the worst flag in the
|
|
15
|
+
// CLI to resolve silently.
|
|
8
16
|
export const GLOBAL_FLAGS = {
|
|
9
17
|
help: 'boolean',
|
|
10
18
|
json: 'boolean',
|
|
11
19
|
verbose: 'boolean',
|
|
12
20
|
yes: 'boolean',
|
|
13
21
|
y: 'boolean',
|
|
22
|
+
version: 'boolean',
|
|
23
|
+
org: 'string',
|
|
14
24
|
};
|
|
25
|
+
// Value flags that name one target rather than a list, mapped to the noun used
|
|
26
|
+
// when refusing a duplicate. Comma-separating these is not a workaround.
|
|
27
|
+
const SINGLE_VALUED = new Map([['org', 'organisation']]);
|
|
28
|
+
// Short aliases are consumed as `-h` / `-v` / `-V` before the `--flag` branch,
|
|
29
|
+
// so they never appear as keys in `merged` and need no type — but they do land
|
|
30
|
+
// in `flags` (as help/version), so the dispatcher must still tolerate them.
|
|
31
|
+
const SHORT_ALIASES = ['h', 'v', 'V'];
|
|
32
|
+
// What the dispatcher tolerates on any command, derived from the list above so
|
|
33
|
+
// the two cannot disagree. They previously drifted: `--org` was missing here
|
|
34
|
+
// while being honoured, so the CLI announced "unknown flag --org (took value
|
|
35
|
+
// …)" and then switched organisation anyway. A user who believed the warning
|
|
36
|
+
// and dropped the flag would have written to whatever org the config pointed
|
|
37
|
+
// at (ImmoPilot, 2026-08-22).
|
|
38
|
+
export const DISPATCH_FLAGS = new Set([
|
|
39
|
+
...Object.keys(GLOBAL_FLAGS),
|
|
40
|
+
...SHORT_ALIASES,
|
|
41
|
+
]);
|
|
15
42
|
// `quiet` suppresses the unknown-flag note. The dispatcher parses twice — once
|
|
16
43
|
// to learn the command, once under that command's own schema — and only the
|
|
17
44
|
// second pass knows which flags are genuinely foreign, so the first must stay
|
|
@@ -110,10 +137,20 @@ export function parseFlags(argv, schema = {}, quiet = false) {
|
|
|
110
137
|
// mistake when the docs say "comma-separated"; silently honouring the
|
|
111
138
|
// last is the one behaviour that can't be recovered from. Refuse, and
|
|
112
139
|
// say what to do instead.
|
|
140
|
+
//
|
|
141
|
+
// The comma-separated suggestion is right for a flag that names a list of
|
|
142
|
+
// things and wrong for one that names a single target: a command writes to
|
|
143
|
+
// one organisation, so `--org a,b` would just build an id that matches no
|
|
144
|
+
// org. Say which case this is rather than offering advice that cannot work.
|
|
113
145
|
if (seenValueFlags.has(key)) {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
146
|
+
const previous = flags[key];
|
|
147
|
+
throw new Error(SINGLE_VALUED.has(key)
|
|
148
|
+
? `--${key} was given more than once ("${String(previous)}", then "${raw}"). ` +
|
|
149
|
+
`Only the last would be used, which silently discards the first.\n` +
|
|
150
|
+
`→ A command acts on one ${SINGLE_VALUED.get(key)}. Pass --${key} once.`
|
|
151
|
+
: `--${key} was given more than once. Only the last value would be used, ` +
|
|
152
|
+
`which silently discards the others.\n` +
|
|
153
|
+
`→ If you meant to pass several values, comma-separate them: --${key} a,b,c`);
|
|
117
154
|
}
|
|
118
155
|
seenValueFlags.add(key);
|
|
119
156
|
if (type === 'number') {
|
package/dist/flags.test.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { parseFlags } from './flags.js';
|
|
2
|
+
import { parseFlags, GLOBAL_FLAGS, DISPATCH_FLAGS } from './flags.js';
|
|
3
3
|
const SCHEMA = {
|
|
4
4
|
org: 'string',
|
|
5
5
|
name: 'string',
|
|
@@ -190,3 +190,42 @@ describe('parseFlags — repeated flags are never silently collapsed', () => {
|
|
|
190
190
|
expect(flags.name).toBe('prod');
|
|
191
191
|
});
|
|
192
192
|
});
|
|
193
|
+
// ImmoPilot, 2026-08-22: `--org` was honoured while being announced as an
|
|
194
|
+
// unknown flag, because the dispatcher's tolerated-flag list and the parser's
|
|
195
|
+
// typed-flag list were maintained separately and disagreed. DISPATCH_FLAGS is
|
|
196
|
+
// now derived from GLOBAL_FLAGS, so this asserts the derivation holds rather
|
|
197
|
+
// than re-checking a hand-written list.
|
|
198
|
+
describe('global flag lists cannot drift', () => {
|
|
199
|
+
it('every globally-typed flag is tolerated by the dispatcher', () => {
|
|
200
|
+
for (const key of Object.keys(GLOBAL_FLAGS)) {
|
|
201
|
+
expect(DISPATCH_FLAGS.has(key)).toBe(true);
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
it('org is typed globally, so every command refuses a duplicate', () => {
|
|
205
|
+
// Ten commands declared `org` in their own schema and got this guard; the
|
|
206
|
+
// other nineteen silently kept the last value. Parse with an empty schema
|
|
207
|
+
// to stand in for a command that declares nothing of its own.
|
|
208
|
+
expect(() => parseFlags(['--org', 'ORG_A', '--org', 'ORG_B'], {}, true)).toThrow(/--org was given more than once/);
|
|
209
|
+
// The generic advice ("comma-separate them") cannot work for org: a command
|
|
210
|
+
// writes to one organisation, and --org a,b is just an id that matches none.
|
|
211
|
+
let msg = '';
|
|
212
|
+
try {
|
|
213
|
+
parseFlags(['--org', 'ORG_A', '--org', 'ORG_B'], {}, true);
|
|
214
|
+
}
|
|
215
|
+
catch (e) {
|
|
216
|
+
msg = e.message;
|
|
217
|
+
}
|
|
218
|
+
expect(msg).not.toContain('comma-separate');
|
|
219
|
+
expect(msg).toContain('acts on one organisation');
|
|
220
|
+
expect(msg).toContain('ORG_A');
|
|
221
|
+
expect(msg).toContain('ORG_B');
|
|
222
|
+
});
|
|
223
|
+
it('a single --org still parses to its value under an empty schema', () => {
|
|
224
|
+
const { flags } = parseFlags(['--org', 'ORG_A'], {}, true);
|
|
225
|
+
expect(flags.org).toBe('ORG_A');
|
|
226
|
+
});
|
|
227
|
+
it('--org is not reported as a foreign flag', () => {
|
|
228
|
+
const { unknownFlags } = parseFlags(['--org', 'ORG_A'], {}, true);
|
|
229
|
+
expect(unknownFlags ?? []).not.toContain('--org');
|
|
230
|
+
});
|
|
231
|
+
});
|
package/dist/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import { MyApiError, setUserAgent, hq } from '@myapihq/sdk';
|
|
|
5
5
|
import { friendlyError } from './errors.js';
|
|
6
6
|
import { currentOrg, adoptSoleOrg } from './helpers.js';
|
|
7
7
|
import * as fs from 'fs';
|
|
8
|
-
import { parseFlags } from './flags.js';
|
|
8
|
+
import { parseFlags, DISPATCH_FLAGS } from './flags.js';
|
|
9
9
|
const pkgPath = new URL('../package.json', import.meta.url);
|
|
10
10
|
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
11
11
|
// Identify the CLI (and its version) on every outbound request. Without this
|
|
@@ -100,8 +100,7 @@ const COMMAND_SCHEMAS = {
|
|
|
100
100
|
webhook: webhookCmd.SCHEMA,
|
|
101
101
|
workflow: workflowCmd.SCHEMA,
|
|
102
102
|
};
|
|
103
|
-
// Flags the dispatcher itself consumes
|
|
104
|
-
const DISPATCH_FLAGS = new Set(['help', 'h', 'json', 'verbose', 'yes', 'y', 'version', 'v', 'V', 'org']);
|
|
103
|
+
// Flags the dispatcher itself consumes are derived from GLOBAL_FLAGS in flags.ts.
|
|
105
104
|
function warnForeignFlags(command, flags) {
|
|
106
105
|
if (!command || process.env.MYAPI_QUIET_UNKNOWN_FLAGS)
|
|
107
106
|
return;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@myapihq/cli",
|
|
3
3
|
"license": "Apache-2.0",
|
|
4
|
-
"version": "2.31.
|
|
4
|
+
"version": "2.31.3",
|
|
5
5
|
"description": "MyAPI command-line interface",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"lint:skills:strict": "node scripts/copy-skills.js && node scripts/lint-skills.js --strict"
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"@myapihq/sdk": "^2.31.
|
|
50
|
+
"@myapihq/sdk": "^2.31.3"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
53
|
"@types/node": "^25.6.0",
|