@quonfig/cli 0.0.8 → 0.0.10
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 +237 -28
- package/dist/commands/info.d.ts +1 -0
- package/dist/commands/info.js +1 -0
- package/dist/commands/list.d.ts +1 -0
- package/dist/commands/list.js +1 -0
- package/dist/commands/set-default.d.ts +1 -0
- package/dist/commands/set-default.js +1 -0
- package/dist/commands/set-rollout.d.ts +18 -0
- package/dist/commands/set-rollout.js +62 -24
- package/dist/hooks/init/friction-log.d.ts +3 -0
- package/dist/hooks/init/friction-log.js +28 -0
- package/dist/init/samples.js +0 -1
- package/dist/util/friction-log.d.ts +17 -0
- package/dist/util/friction-log.js +56 -0
- package/dist/util/oauth-client.js +2 -2
- package/dist/verify/validate.js +34 -1
- package/oclif.manifest.json +11 -4
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ $ npm install -g @quonfig/cli
|
|
|
14
14
|
$ qfg COMMAND
|
|
15
15
|
running command...
|
|
16
16
|
$ qfg (--version)
|
|
17
|
-
@quonfig/cli/0.0.
|
|
17
|
+
@quonfig/cli/0.0.10 linux-x64 node-v24.4.1
|
|
18
18
|
$ qfg --help [COMMAND]
|
|
19
19
|
USAGE
|
|
20
20
|
$ qfg COMMAND
|
|
@@ -27,6 +27,9 @@ USAGE
|
|
|
27
27
|
<!-- commands -->
|
|
28
28
|
* [`qfg config-schema`](#qfg-config-schema)
|
|
29
29
|
* [`qfg create NAME`](#qfg-create-name)
|
|
30
|
+
* [`qfg flag info [NAME]`](#qfg-flag-info-name)
|
|
31
|
+
* [`qfg flag list`](#qfg-flag-list)
|
|
32
|
+
* [`qfg flag show [NAME]`](#qfg-flag-show-name)
|
|
30
33
|
* [`qfg generate`](#qfg-generate)
|
|
31
34
|
* [`qfg generate-new-hex-key`](#qfg-generate-new-hex-key)
|
|
32
35
|
* [`qfg get [NAME]`](#qfg-get-name)
|
|
@@ -47,6 +50,7 @@ USAGE
|
|
|
47
50
|
* [`qfg set-default [NAME]`](#qfg-set-default-name)
|
|
48
51
|
* [`qfg set-rollout [NAME]`](#qfg-set-rollout-name)
|
|
49
52
|
* [`qfg sync`](#qfg-sync)
|
|
53
|
+
* [`qfg toggle [NAME]`](#qfg-toggle-name)
|
|
50
54
|
* [`qfg verify [PATH]`](#qfg-verify-path)
|
|
51
55
|
* [`qfg whoami`](#qfg-whoami)
|
|
52
56
|
* [`qfg workspace`](#qfg-workspace)
|
|
@@ -80,7 +84,7 @@ EXAMPLES
|
|
|
80
84
|
$ qfg config-schema --json-schema # full JSON Schema document (copy into your editor)
|
|
81
85
|
```
|
|
82
86
|
|
|
83
|
-
_See code: [src/commands/config-schema.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
87
|
+
_See code: [src/commands/config-schema.ts](https://github.com/quonfig/cli/blob/v0.0.10/src/commands/config-schema.ts)_
|
|
84
88
|
|
|
85
89
|
## `qfg create NAME`
|
|
86
90
|
|
|
@@ -152,7 +156,137 @@ EXAMPLES
|
|
|
152
156
|
$ qfg set-rollout my.new.flag --environment production --true-percent 20
|
|
153
157
|
```
|
|
154
158
|
|
|
155
|
-
_See code: [src/commands/create.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
159
|
+
_See code: [src/commands/create.ts](https://github.com/quonfig/cli/blob/v0.0.10/src/commands/create.ts)_
|
|
160
|
+
|
|
161
|
+
## `qfg flag info [NAME]`
|
|
162
|
+
|
|
163
|
+
Show current values, rules, and evaluation stats for a flag or config.
|
|
164
|
+
|
|
165
|
+
```
|
|
166
|
+
USAGE
|
|
167
|
+
$ qfg flag info [NAME] [--json] [--interactive] [--no-color] [--verbose] [-w <value>] [--exclude-evaluations]
|
|
168
|
+
|
|
169
|
+
ARGUMENTS
|
|
170
|
+
NAME config/feature-flag/etc. name
|
|
171
|
+
|
|
172
|
+
FLAGS
|
|
173
|
+
--exclude-evaluations Exclude evaluation data
|
|
174
|
+
|
|
175
|
+
GLOBAL FLAGS
|
|
176
|
+
-w, --workspace=<value> Workspace slug to use (overrides QUONFIG_WORKSPACE env var and saved default)
|
|
177
|
+
--[no-]interactive Force interactive mode
|
|
178
|
+
--json Format output as json.
|
|
179
|
+
--no-color Do not colorize output
|
|
180
|
+
--verbose Verbose output
|
|
181
|
+
|
|
182
|
+
DESCRIPTION
|
|
183
|
+
Show current values, rules, and evaluation stats for a flag or config.
|
|
184
|
+
|
|
185
|
+
Output shows the value for each environment:
|
|
186
|
+
- A simple value (true / false / "hello") means a single unconditional rule.
|
|
187
|
+
- "[see rules]" means the environment has targeting rules or a percentage rollout.
|
|
188
|
+
- "[inherit]" means the environment falls back to the Default value.
|
|
189
|
+
|
|
190
|
+
Percentage rollouts are displayed as "20.0% true, 80.0% false".
|
|
191
|
+
Evaluation counts for the past 24 hours are included by default.
|
|
192
|
+
|
|
193
|
+
Related commands:
|
|
194
|
+
qfg set-default my.flag --environment production --value true # set a scalar fallback
|
|
195
|
+
qfg set-rollout my.flag --environment production --true-percent 20 # set a % rollout
|
|
196
|
+
|
|
197
|
+
ALIASES
|
|
198
|
+
$ qfg flag show
|
|
199
|
+
$ qfg flag info
|
|
200
|
+
|
|
201
|
+
EXAMPLES
|
|
202
|
+
$ qfg flag info my.config.name
|
|
203
|
+
|
|
204
|
+
$ qfg flag info my.config.name --exclude-evaluations # skip 24h stats
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## `qfg flag list`
|
|
208
|
+
|
|
209
|
+
Show keys for your config/feature flags/etc.
|
|
210
|
+
|
|
211
|
+
```
|
|
212
|
+
USAGE
|
|
213
|
+
$ qfg flag list [--json] [--interactive] [--no-color] [--verbose] [-w <value>] [--configs] [--feature-flags]
|
|
214
|
+
[--log-levels] [--schemas] [--segments]
|
|
215
|
+
|
|
216
|
+
FLAGS
|
|
217
|
+
--configs include configs
|
|
218
|
+
--feature-flags include flags
|
|
219
|
+
--log-levels include log levels
|
|
220
|
+
--schemas include schemas
|
|
221
|
+
--segments include segments
|
|
222
|
+
|
|
223
|
+
GLOBAL FLAGS
|
|
224
|
+
-w, --workspace=<value> Workspace slug to use (overrides QUONFIG_WORKSPACE env var and saved default)
|
|
225
|
+
--[no-]interactive Force interactive mode
|
|
226
|
+
--json Format output as json.
|
|
227
|
+
--no-color Do not colorize output
|
|
228
|
+
--verbose Verbose output
|
|
229
|
+
|
|
230
|
+
DESCRIPTION
|
|
231
|
+
Show keys for your config/feature flags/etc.
|
|
232
|
+
|
|
233
|
+
All types are returned by default. If you pass one or more type flags (e.g. --configs), only those types will be
|
|
234
|
+
returned
|
|
235
|
+
|
|
236
|
+
ALIASES
|
|
237
|
+
$ qfg flag list
|
|
238
|
+
|
|
239
|
+
EXAMPLES
|
|
240
|
+
$ qfg flag list
|
|
241
|
+
|
|
242
|
+
$ qfg flag list --feature-flags
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
## `qfg flag show [NAME]`
|
|
246
|
+
|
|
247
|
+
Show current values, rules, and evaluation stats for a flag or config.
|
|
248
|
+
|
|
249
|
+
```
|
|
250
|
+
USAGE
|
|
251
|
+
$ qfg flag show [NAME] [--json] [--interactive] [--no-color] [--verbose] [-w <value>] [--exclude-evaluations]
|
|
252
|
+
|
|
253
|
+
ARGUMENTS
|
|
254
|
+
NAME config/feature-flag/etc. name
|
|
255
|
+
|
|
256
|
+
FLAGS
|
|
257
|
+
--exclude-evaluations Exclude evaluation data
|
|
258
|
+
|
|
259
|
+
GLOBAL FLAGS
|
|
260
|
+
-w, --workspace=<value> Workspace slug to use (overrides QUONFIG_WORKSPACE env var and saved default)
|
|
261
|
+
--[no-]interactive Force interactive mode
|
|
262
|
+
--json Format output as json.
|
|
263
|
+
--no-color Do not colorize output
|
|
264
|
+
--verbose Verbose output
|
|
265
|
+
|
|
266
|
+
DESCRIPTION
|
|
267
|
+
Show current values, rules, and evaluation stats for a flag or config.
|
|
268
|
+
|
|
269
|
+
Output shows the value for each environment:
|
|
270
|
+
- A simple value (true / false / "hello") means a single unconditional rule.
|
|
271
|
+
- "[see rules]" means the environment has targeting rules or a percentage rollout.
|
|
272
|
+
- "[inherit]" means the environment falls back to the Default value.
|
|
273
|
+
|
|
274
|
+
Percentage rollouts are displayed as "20.0% true, 80.0% false".
|
|
275
|
+
Evaluation counts for the past 24 hours are included by default.
|
|
276
|
+
|
|
277
|
+
Related commands:
|
|
278
|
+
qfg set-default my.flag --environment production --value true # set a scalar fallback
|
|
279
|
+
qfg set-rollout my.flag --environment production --true-percent 20 # set a % rollout
|
|
280
|
+
|
|
281
|
+
ALIASES
|
|
282
|
+
$ qfg flag show
|
|
283
|
+
$ qfg flag info
|
|
284
|
+
|
|
285
|
+
EXAMPLES
|
|
286
|
+
$ qfg flag show my.config.name
|
|
287
|
+
|
|
288
|
+
$ qfg flag show my.config.name --exclude-evaluations # skip 24h stats
|
|
289
|
+
```
|
|
156
290
|
|
|
157
291
|
## `qfg generate`
|
|
158
292
|
|
|
@@ -222,7 +356,7 @@ EXAMPLES
|
|
|
222
356
|
$ qfg generate --targets node-ts -o ./dist # combine with targets
|
|
223
357
|
```
|
|
224
358
|
|
|
225
|
-
_See code: [src/commands/generate.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
359
|
+
_See code: [src/commands/generate.ts](https://github.com/quonfig/cli/blob/v0.0.10/src/commands/generate.ts)_
|
|
226
360
|
|
|
227
361
|
## `qfg generate-new-hex-key`
|
|
228
362
|
|
|
@@ -245,7 +379,7 @@ EXAMPLES
|
|
|
245
379
|
$ qfg generate-new-hex-key
|
|
246
380
|
```
|
|
247
381
|
|
|
248
|
-
_See code: [src/commands/generate-new-hex-key.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
382
|
+
_See code: [src/commands/generate-new-hex-key.ts](https://github.com/quonfig/cli/blob/v0.0.10/src/commands/generate-new-hex-key.ts)_
|
|
249
383
|
|
|
250
384
|
## `qfg get [NAME]`
|
|
251
385
|
|
|
@@ -277,7 +411,7 @@ EXAMPLES
|
|
|
277
411
|
$ qfg get my.config.name --environment=production
|
|
278
412
|
```
|
|
279
413
|
|
|
280
|
-
_See code: [src/commands/get.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
414
|
+
_See code: [src/commands/get.ts](https://github.com/quonfig/cli/blob/v0.0.10/src/commands/get.ts)_
|
|
281
415
|
|
|
282
416
|
## `qfg info [NAME]`
|
|
283
417
|
|
|
@@ -315,13 +449,17 @@ DESCRIPTION
|
|
|
315
449
|
qfg set-default my.flag --environment production --value true # set a scalar fallback
|
|
316
450
|
qfg set-rollout my.flag --environment production --true-percent 20 # set a % rollout
|
|
317
451
|
|
|
452
|
+
ALIASES
|
|
453
|
+
$ qfg flag show
|
|
454
|
+
$ qfg flag info
|
|
455
|
+
|
|
318
456
|
EXAMPLES
|
|
319
457
|
$ qfg info my.config.name
|
|
320
458
|
|
|
321
459
|
$ qfg info my.config.name --exclude-evaluations # skip 24h stats
|
|
322
460
|
```
|
|
323
461
|
|
|
324
|
-
_See code: [src/commands/info.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
462
|
+
_See code: [src/commands/info.ts](https://github.com/quonfig/cli/blob/v0.0.10/src/commands/info.ts)_
|
|
325
463
|
|
|
326
464
|
## `qfg init [DIRECTORY]`
|
|
327
465
|
|
|
@@ -359,7 +497,7 @@ EXAMPLES
|
|
|
359
497
|
$ qfg init --dry-run
|
|
360
498
|
```
|
|
361
499
|
|
|
362
|
-
_See code: [src/commands/init.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
500
|
+
_See code: [src/commands/init.ts](https://github.com/quonfig/cli/blob/v0.0.10/src/commands/init.ts)_
|
|
363
501
|
|
|
364
502
|
## `qfg interactive`
|
|
365
503
|
|
|
@@ -377,7 +515,7 @@ EXAMPLES
|
|
|
377
515
|
$ qfg
|
|
378
516
|
```
|
|
379
517
|
|
|
380
|
-
_See code: [src/commands/interactive.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
518
|
+
_See code: [src/commands/interactive.ts](https://github.com/quonfig/cli/blob/v0.0.10/src/commands/interactive.ts)_
|
|
381
519
|
|
|
382
520
|
## `qfg list`
|
|
383
521
|
|
|
@@ -408,13 +546,16 @@ DESCRIPTION
|
|
|
408
546
|
All types are returned by default. If you pass one or more type flags (e.g. --configs), only those types will be
|
|
409
547
|
returned
|
|
410
548
|
|
|
549
|
+
ALIASES
|
|
550
|
+
$ qfg flag list
|
|
551
|
+
|
|
411
552
|
EXAMPLES
|
|
412
553
|
$ qfg list
|
|
413
554
|
|
|
414
555
|
$ qfg list --feature-flags
|
|
415
556
|
```
|
|
416
557
|
|
|
417
|
-
_See code: [src/commands/list.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
558
|
+
_See code: [src/commands/list.ts](https://github.com/quonfig/cli/blob/v0.0.10/src/commands/list.ts)_
|
|
418
559
|
|
|
419
560
|
## `qfg login`
|
|
420
561
|
|
|
@@ -439,7 +580,7 @@ EXAMPLES
|
|
|
439
580
|
$ qfg login --profile myprofile
|
|
440
581
|
```
|
|
441
582
|
|
|
442
|
-
_See code: [src/commands/login.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
583
|
+
_See code: [src/commands/login.ts](https://github.com/quonfig/cli/blob/v0.0.10/src/commands/login.ts)_
|
|
443
584
|
|
|
444
585
|
## `qfg logout`
|
|
445
586
|
|
|
@@ -462,7 +603,7 @@ EXAMPLES
|
|
|
462
603
|
$ qfg logout
|
|
463
604
|
```
|
|
464
605
|
|
|
465
|
-
_See code: [src/commands/logout.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
606
|
+
_See code: [src/commands/logout.ts](https://github.com/quonfig/cli/blob/v0.0.10/src/commands/logout.ts)_
|
|
466
607
|
|
|
467
608
|
## `qfg mcp`
|
|
468
609
|
|
|
@@ -495,7 +636,7 @@ EXAMPLES
|
|
|
495
636
|
$ qfg mcp --url http://local-app.quonfig-staging.com:3003/api/v1/mcp
|
|
496
637
|
```
|
|
497
638
|
|
|
498
|
-
_See code: [src/commands/mcp.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
639
|
+
_See code: [src/commands/mcp.ts](https://github.com/quonfig/cli/blob/v0.0.10/src/commands/mcp.ts)_
|
|
499
640
|
|
|
500
641
|
## `qfg override [NAME]`
|
|
501
642
|
|
|
@@ -534,7 +675,7 @@ EXAMPLES
|
|
|
534
675
|
$ qfg override my.double.config --value=3.14159
|
|
535
676
|
```
|
|
536
677
|
|
|
537
|
-
_See code: [src/commands/override.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
678
|
+
_See code: [src/commands/override.ts](https://github.com/quonfig/cli/blob/v0.0.10/src/commands/override.ts)_
|
|
538
679
|
|
|
539
680
|
## `qfg pull`
|
|
540
681
|
|
|
@@ -578,7 +719,7 @@ EXAMPLES
|
|
|
578
719
|
$ qfg pull # uses QUONFIG_DIR env var
|
|
579
720
|
```
|
|
580
721
|
|
|
581
|
-
_See code: [src/commands/pull.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
722
|
+
_See code: [src/commands/pull.ts](https://github.com/quonfig/cli/blob/v0.0.10/src/commands/pull.ts)_
|
|
582
723
|
|
|
583
724
|
## `qfg schema NAME`
|
|
584
725
|
|
|
@@ -616,7 +757,7 @@ EXAMPLES
|
|
|
616
757
|
$ qfg schema my-schema --set-json-schema=@schemas/my-schema.json --protected
|
|
617
758
|
```
|
|
618
759
|
|
|
619
|
-
_See code: [src/commands/schema.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
760
|
+
_See code: [src/commands/schema.ts](https://github.com/quonfig/cli/blob/v0.0.10/src/commands/schema.ts)_
|
|
620
761
|
|
|
621
762
|
## `qfg sdk-key`
|
|
622
763
|
|
|
@@ -643,7 +784,7 @@ EXAMPLES
|
|
|
643
784
|
$ qfg sdk-key revoke <key-id>
|
|
644
785
|
```
|
|
645
786
|
|
|
646
|
-
_See code: [src/commands/sdk-key.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
787
|
+
_See code: [src/commands/sdk-key.ts](https://github.com/quonfig/cli/blob/v0.0.10/src/commands/sdk-key.ts)_
|
|
647
788
|
|
|
648
789
|
## `qfg sdk-key create`
|
|
649
790
|
|
|
@@ -675,7 +816,7 @@ EXAMPLES
|
|
|
675
816
|
$ qfg sdk-key create --environment staging --type browser
|
|
676
817
|
```
|
|
677
818
|
|
|
678
|
-
_See code: [src/commands/sdk-key/create.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
819
|
+
_See code: [src/commands/sdk-key/create.ts](https://github.com/quonfig/cli/blob/v0.0.10/src/commands/sdk-key/create.ts)_
|
|
679
820
|
|
|
680
821
|
## `qfg sdk-key list`
|
|
681
822
|
|
|
@@ -704,7 +845,7 @@ EXAMPLES
|
|
|
704
845
|
$ qfg sdk-key list --environment production
|
|
705
846
|
```
|
|
706
847
|
|
|
707
|
-
_See code: [src/commands/sdk-key/list.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
848
|
+
_See code: [src/commands/sdk-key/list.ts](https://github.com/quonfig/cli/blob/v0.0.10/src/commands/sdk-key/list.ts)_
|
|
708
849
|
|
|
709
850
|
## `qfg sdk-key revoke KEYID`
|
|
710
851
|
|
|
@@ -731,7 +872,7 @@ EXAMPLES
|
|
|
731
872
|
$ qfg sdk-key revoke a1b2c3d4-e5f6-7890-abcd-ef1234567890
|
|
732
873
|
```
|
|
733
874
|
|
|
734
|
-
_See code: [src/commands/sdk-key/revoke.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
875
|
+
_See code: [src/commands/sdk-key/revoke.ts](https://github.com/quonfig/cli/blob/v0.0.10/src/commands/sdk-key/revoke.ts)_
|
|
735
876
|
|
|
736
877
|
## `qfg set-default [NAME]`
|
|
737
878
|
|
|
@@ -779,6 +920,9 @@ DESCRIPTION
|
|
|
779
920
|
To see all current values and rules for a flag:
|
|
780
921
|
qfg info my.flag
|
|
781
922
|
|
|
923
|
+
ALIASES
|
|
924
|
+
$ qfg toggle
|
|
925
|
+
|
|
782
926
|
EXAMPLES
|
|
783
927
|
$ qfg set-default my.flag.name # prompts for value and env
|
|
784
928
|
|
|
@@ -795,7 +939,7 @@ EXAMPLES
|
|
|
795
939
|
$ qfg set-rollout my.flag.name --environment production --true-percent 20
|
|
796
940
|
```
|
|
797
941
|
|
|
798
|
-
_See code: [src/commands/set-default.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
942
|
+
_See code: [src/commands/set-default.ts](https://github.com/quonfig/cli/blob/v0.0.10/src/commands/set-default.ts)_
|
|
799
943
|
|
|
800
944
|
## `qfg set-rollout [NAME]`
|
|
801
945
|
|
|
@@ -872,7 +1016,7 @@ EXAMPLES
|
|
|
872
1016
|
$ qfg set-rollout my.variant.flag --environment production --weights "a:33,b:33,c:34" --hash-by user.id
|
|
873
1017
|
```
|
|
874
1018
|
|
|
875
|
-
_See code: [src/commands/set-rollout.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1019
|
+
_See code: [src/commands/set-rollout.ts](https://github.com/quonfig/cli/blob/v0.0.10/src/commands/set-rollout.ts)_
|
|
876
1020
|
|
|
877
1021
|
## `qfg sync`
|
|
878
1022
|
|
|
@@ -904,7 +1048,72 @@ EXAMPLES
|
|
|
904
1048
|
$ qfg sync --watch --dir ./our-config --interval 30
|
|
905
1049
|
```
|
|
906
1050
|
|
|
907
|
-
_See code: [src/commands/sync.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1051
|
+
_See code: [src/commands/sync.ts](https://github.com/quonfig/cli/blob/v0.0.10/src/commands/sync.ts)_
|
|
1052
|
+
|
|
1053
|
+
## `qfg toggle [NAME]`
|
|
1054
|
+
|
|
1055
|
+
Set the unconditional fallback value for a flag or config in a specific environment.
|
|
1056
|
+
|
|
1057
|
+
```
|
|
1058
|
+
USAGE
|
|
1059
|
+
$ qfg toggle [NAME] [--json] [--interactive] [--no-color] [--verbose] [-w <value>] [--confidential]
|
|
1060
|
+
[--env-var <value>] [--environment <value>] [--value <value>] [--confirm] [--secret] [--secret-key-name <value>]
|
|
1061
|
+
|
|
1062
|
+
ARGUMENTS
|
|
1063
|
+
NAME config/feature-flag/etc. name
|
|
1064
|
+
|
|
1065
|
+
FLAGS
|
|
1066
|
+
--confidential mark the value as confidential
|
|
1067
|
+
--confirm confirm without prompt
|
|
1068
|
+
--env-var=<value> environment variable to use as default value
|
|
1069
|
+
--environment=<value> environment to change
|
|
1070
|
+
--secret encrypt the value of this item
|
|
1071
|
+
--secret-key-name=<value> [default: quonfig.secrets.encryption.key] name of the secret key to use for
|
|
1072
|
+
encryption/decryption
|
|
1073
|
+
--value=<value> new default value
|
|
1074
|
+
|
|
1075
|
+
GLOBAL FLAGS
|
|
1076
|
+
-w, --workspace=<value> Workspace slug to use (overrides QUONFIG_WORKSPACE env var and saved default)
|
|
1077
|
+
--[no-]interactive Force interactive mode
|
|
1078
|
+
--json Format output as json.
|
|
1079
|
+
--no-color Do not colorize output
|
|
1080
|
+
--verbose Verbose output
|
|
1081
|
+
|
|
1082
|
+
DESCRIPTION
|
|
1083
|
+
Set the unconditional fallback value for a flag or config in a specific environment.
|
|
1084
|
+
|
|
1085
|
+
This updates the catch-all rule — the value users receive when NO targeting rule matches.
|
|
1086
|
+
Any targeting rules or percentage rollouts you have configured are NOT affected; they
|
|
1087
|
+
continue to fire before this fallback is evaluated.
|
|
1088
|
+
|
|
1089
|
+
"Other rules still apply" means: if you have rules targeting specific users, segments,
|
|
1090
|
+
or properties, those rules still take priority. This command only changes what everyone
|
|
1091
|
+
else sees.
|
|
1092
|
+
|
|
1093
|
+
To set a percentage rollout (gradual rollout / A/B test / canary deploy) instead:
|
|
1094
|
+
qfg set-rollout my.flag --environment production --true-percent 20
|
|
1095
|
+
|
|
1096
|
+
To see all current values and rules for a flag:
|
|
1097
|
+
qfg info my.flag
|
|
1098
|
+
|
|
1099
|
+
ALIASES
|
|
1100
|
+
$ qfg toggle
|
|
1101
|
+
|
|
1102
|
+
EXAMPLES
|
|
1103
|
+
$ qfg toggle my.flag.name # prompts for value and env
|
|
1104
|
+
|
|
1105
|
+
$ qfg toggle my.flag.name --value=true --environment=staging
|
|
1106
|
+
|
|
1107
|
+
$ qfg toggle my.flag.name --value=false --environment=production # kill-switch: turn off for everyone
|
|
1108
|
+
|
|
1109
|
+
$ qfg toggle my.flag.name --value=true --secret
|
|
1110
|
+
|
|
1111
|
+
$ qfg toggle my.config.name --env-var=MY_ENV_VAR_NAME --environment=production
|
|
1112
|
+
|
|
1113
|
+
# For a percentage rollout, use set-rollout instead:
|
|
1114
|
+
|
|
1115
|
+
$ qfg set-rollout my.flag.name --environment production --true-percent 20
|
|
1116
|
+
```
|
|
908
1117
|
|
|
909
1118
|
## `qfg verify [PATH]`
|
|
910
1119
|
|
|
@@ -937,7 +1146,7 @@ EXAMPLES
|
|
|
937
1146
|
$ qfg verify --json
|
|
938
1147
|
```
|
|
939
1148
|
|
|
940
|
-
_See code: [src/commands/verify.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1149
|
+
_See code: [src/commands/verify.ts](https://github.com/quonfig/cli/blob/v0.0.10/src/commands/verify.ts)_
|
|
941
1150
|
|
|
942
1151
|
## `qfg whoami`
|
|
943
1152
|
|
|
@@ -960,7 +1169,7 @@ EXAMPLES
|
|
|
960
1169
|
$ qfg whoami
|
|
961
1170
|
```
|
|
962
1171
|
|
|
963
|
-
_See code: [src/commands/whoami.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1172
|
+
_See code: [src/commands/whoami.ts](https://github.com/quonfig/cli/blob/v0.0.10/src/commands/whoami.ts)_
|
|
964
1173
|
|
|
965
1174
|
## `qfg workspace`
|
|
966
1175
|
|
|
@@ -983,7 +1192,7 @@ EXAMPLES
|
|
|
983
1192
|
$ qfg workspace
|
|
984
1193
|
```
|
|
985
1194
|
|
|
986
|
-
_See code: [src/commands/workspace.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1195
|
+
_See code: [src/commands/workspace.ts](https://github.com/quonfig/cli/blob/v0.0.10/src/commands/workspace.ts)_
|
|
987
1196
|
|
|
988
1197
|
## `qfg workspace bootstrap`
|
|
989
1198
|
|
|
@@ -1016,7 +1225,7 @@ EXAMPLES
|
|
|
1016
1225
|
$ qfg workspace bootstrap --dir ./our-config --skip-validate
|
|
1017
1226
|
```
|
|
1018
1227
|
|
|
1019
|
-
_See code: [src/commands/workspace/bootstrap.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1228
|
+
_See code: [src/commands/workspace/bootstrap.ts](https://github.com/quonfig/cli/blob/v0.0.10/src/commands/workspace/bootstrap.ts)_
|
|
1020
1229
|
|
|
1021
1230
|
## `qfg workspace switch`
|
|
1022
1231
|
|
|
@@ -1039,7 +1248,7 @@ EXAMPLES
|
|
|
1039
1248
|
$ qfg workspace switch
|
|
1040
1249
|
```
|
|
1041
1250
|
|
|
1042
|
-
_See code: [src/commands/workspace/switch.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1251
|
+
_See code: [src/commands/workspace/switch.ts](https://github.com/quonfig/cli/blob/v0.0.10/src/commands/workspace/switch.ts)_
|
|
1043
1252
|
<!-- commandsstop -->
|
|
1044
1253
|
|
|
1045
1254
|
## Local Development
|
package/dist/commands/info.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { APICommand } from '../index.js';
|
|
2
2
|
import { JsonObj } from '../result.js';
|
|
3
3
|
export default class Info extends APICommand {
|
|
4
|
+
static aliases: string[];
|
|
4
5
|
static args: {
|
|
5
6
|
name: import("@oclif/core/lib/interfaces/parser.js").Arg<string | undefined, Record<string, unknown>>;
|
|
6
7
|
};
|
package/dist/commands/info.js
CHANGED
|
@@ -6,6 +6,7 @@ import { getAppUrl } from '../util/domain-urls.js';
|
|
|
6
6
|
import isInteractive from '../util/is-interactive.js';
|
|
7
7
|
import nameArg from '../util/name-arg.js';
|
|
8
8
|
export default class Info extends APICommand {
|
|
9
|
+
static aliases = ['flag:show', 'flag:info'];
|
|
9
10
|
static args = { ...nameArg };
|
|
10
11
|
static description = `Show current values, rules, and evaluation stats for a flag or config.
|
|
11
12
|
|
package/dist/commands/list.d.ts
CHANGED
package/dist/commands/list.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Flags } from '@oclif/core';
|
|
2
2
|
import { APICommand } from '../index.js';
|
|
3
3
|
export default class List extends APICommand {
|
|
4
|
+
static aliases = ['flag:list'];
|
|
4
5
|
static description = `Show keys for your config/feature flags/etc.
|
|
5
6
|
|
|
6
7
|
All types are returned by default. If you pass one or more type flags (e.g. --configs), only those types will be returned`;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { APICommand } from '../index.js';
|
|
2
2
|
import { JsonObj } from '../result.js';
|
|
3
3
|
export default class SetDefault extends APICommand {
|
|
4
|
+
static aliases: string[];
|
|
4
5
|
static args: {
|
|
5
6
|
name: import("@oclif/core/lib/interfaces/parser.js").Arg<string | undefined, Record<string, unknown>>;
|
|
6
7
|
};
|
|
@@ -13,6 +13,7 @@ import isInteractive from '../util/is-interactive.js';
|
|
|
13
13
|
import nameArg from '../util/name-arg.js';
|
|
14
14
|
import secretFlags, { parsedSecretFlags } from '../util/secret-flags.js';
|
|
15
15
|
export default class SetDefault extends APICommand {
|
|
16
|
+
static aliases = ['toggle'];
|
|
16
17
|
static args = { ...nameArg };
|
|
17
18
|
static description = `Set the unconditional fallback value for a flag or config in a specific environment.
|
|
18
19
|
|
|
@@ -17,3 +17,21 @@ export default class SetRollout extends APICommand {
|
|
|
17
17
|
private parseWeights;
|
|
18
18
|
private coerceValue;
|
|
19
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* Pick a short, stable variant name from a rollout value. Uses the first
|
|
22
|
+
* 10 characters of the stringified value, then dedupes collisions by
|
|
23
|
+
* appending `-2`, `-3`, ... so each name is unique within the set.
|
|
24
|
+
*/
|
|
25
|
+
export declare function synthesizeVariants(weightedValues: Array<{
|
|
26
|
+
value: {
|
|
27
|
+
type: string;
|
|
28
|
+
value: unknown;
|
|
29
|
+
};
|
|
30
|
+
weight: number;
|
|
31
|
+
}>): Array<{
|
|
32
|
+
name: string;
|
|
33
|
+
value: {
|
|
34
|
+
type: string;
|
|
35
|
+
value: unknown;
|
|
36
|
+
};
|
|
37
|
+
}>;
|
|
@@ -165,7 +165,20 @@ To inspect the current rollout:
|
|
|
165
165
|
const updatedEnvironments = hasEnv
|
|
166
166
|
? existingEnvs.map((e) => (e.id === envKey ? { ...e, rules: [newRule] } : e))
|
|
167
167
|
: [...existingEnvs, { id: envKey, rules: [newRule] }];
|
|
168
|
-
|
|
168
|
+
// Auto-create variants when the config has none. The UI cannot render a
|
|
169
|
+
// rollout without named variants, and the server/gitea verify now rejects
|
|
170
|
+
// it. Bool configs are exempt — the UI supplies implicit true/false variants.
|
|
171
|
+
const existingVariants = currentConfig.variants ?? [];
|
|
172
|
+
const updateFields = {
|
|
173
|
+
environments: updatedEnvironments,
|
|
174
|
+
};
|
|
175
|
+
if (existingVariants.length === 0 && config.valueType !== 'bool') {
|
|
176
|
+
const synthesized = synthesizeVariants(weightedValues);
|
|
177
|
+
if (synthesized.length > 0) {
|
|
178
|
+
updateFields.variants = synthesized;
|
|
179
|
+
this.log(`${checkmark} Auto-created ${synthesized.length} variant${synthesized.length === 1 ? '' : 's'}: ${synthesized.map((v) => v.name).join(', ')}`);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
169
182
|
this.verboseLog('Update fields:', JSON.stringify(updateFields, null, 2));
|
|
170
183
|
let request;
|
|
171
184
|
if (config.type === 'feature_flag') {
|
|
@@ -229,32 +242,57 @@ To inspect the current rollout:
|
|
|
229
242
|
return { ok: true, weights };
|
|
230
243
|
}
|
|
231
244
|
coerceValue(raw, valueType) {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
245
|
+
return coerceValue(raw, valueType);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Pick a short, stable variant name from a rollout value. Uses the first
|
|
250
|
+
* 10 characters of the stringified value, then dedupes collisions by
|
|
251
|
+
* appending `-2`, `-3`, ... so each name is unique within the set.
|
|
252
|
+
*/
|
|
253
|
+
export function synthesizeVariants(weightedValues) {
|
|
254
|
+
const used = new Set();
|
|
255
|
+
const variants = [];
|
|
256
|
+
for (const wv of weightedValues) {
|
|
257
|
+
const raw = typeof wv.value.value === 'string' ? wv.value.value : JSON.stringify(wv.value.value);
|
|
258
|
+
const base = (raw || wv.value.type).slice(0, 10);
|
|
259
|
+
let name = base;
|
|
260
|
+
let suffix = 2;
|
|
261
|
+
while (used.has(name)) {
|
|
262
|
+
name = `${base}-${suffix}`;
|
|
263
|
+
suffix += 1;
|
|
264
|
+
}
|
|
265
|
+
used.add(name);
|
|
266
|
+
variants.push({ name, value: wv.value });
|
|
267
|
+
}
|
|
268
|
+
return variants;
|
|
269
|
+
}
|
|
270
|
+
function coerceValue(raw, valueType) {
|
|
271
|
+
const type = valueType.toLowerCase();
|
|
272
|
+
switch (type) {
|
|
273
|
+
case 'bool': {
|
|
274
|
+
if (raw !== 'true' && raw !== 'false') {
|
|
275
|
+
return { error: `Invalid boolean value "${raw}". Must be "true" or "false".`, ok: false };
|
|
246
276
|
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
return {
|
|
277
|
+
return { ok: true, typed: { type: 'bool', value: raw === 'true' } };
|
|
278
|
+
}
|
|
279
|
+
case 'int': {
|
|
280
|
+
const n = Number.parseInt(raw, 10);
|
|
281
|
+
if (Number.isNaN(n)) {
|
|
282
|
+
return { error: `Invalid int value "${raw}".`, ok: false };
|
|
253
283
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
284
|
+
return { ok: true, typed: { type: 'int', value: n } };
|
|
285
|
+
}
|
|
286
|
+
case 'double': {
|
|
287
|
+
const n = Number.parseFloat(raw);
|
|
288
|
+
if (Number.isNaN(n)) {
|
|
289
|
+
return { error: `Invalid double value "${raw}".`, ok: false };
|
|
257
290
|
}
|
|
291
|
+
return { ok: true, typed: { type: 'double', value: n } };
|
|
292
|
+
}
|
|
293
|
+
default: {
|
|
294
|
+
// string, json, etc — pass as-is
|
|
295
|
+
return { ok: true, typed: { type, value: raw } };
|
|
258
296
|
}
|
|
259
297
|
}
|
|
260
298
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { appendFrictionEntry, buildFrictionEntry, extractLastErrorLine, getFrictionLogPath, installStderrCapture, } from '../../util/friction-log.js';
|
|
2
|
+
let installed = false;
|
|
3
|
+
const hook = async function ({ config: _config }) {
|
|
4
|
+
if (installed)
|
|
5
|
+
return;
|
|
6
|
+
installed = true;
|
|
7
|
+
const logPath = getFrictionLogPath(process.env.QFG_FRICTION_LOG);
|
|
8
|
+
if (!logPath)
|
|
9
|
+
return;
|
|
10
|
+
const readCapturedStderr = installStderrCapture();
|
|
11
|
+
const originalArgv = process.argv.slice(2);
|
|
12
|
+
process.on('exit', (code) => {
|
|
13
|
+
if (code === 0)
|
|
14
|
+
return;
|
|
15
|
+
try {
|
|
16
|
+
const entry = buildFrictionEntry({
|
|
17
|
+
argv: originalArgv,
|
|
18
|
+
error: extractLastErrorLine(readCapturedStderr()),
|
|
19
|
+
exitCode: code,
|
|
20
|
+
});
|
|
21
|
+
appendFrictionEntry(logPath, entry);
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
// never fail a CLI invocation because of friction logging
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
};
|
|
28
|
+
export default hook;
|
package/dist/init/samples.js
CHANGED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface FrictionEntry {
|
|
2
|
+
attempted: string;
|
|
3
|
+
error: string;
|
|
4
|
+
exitCode: number;
|
|
5
|
+
ts: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function getFrictionLogPath(envValue: string | undefined, cwd?: string): null | string;
|
|
8
|
+
export declare function buildFrictionEntry(opts: {
|
|
9
|
+
argv?: string[];
|
|
10
|
+
binName?: string;
|
|
11
|
+
error?: string;
|
|
12
|
+
exitCode: number;
|
|
13
|
+
now?: Date;
|
|
14
|
+
}): FrictionEntry;
|
|
15
|
+
export declare function appendFrictionEntry(logPath: string, entry: FrictionEntry): void;
|
|
16
|
+
export declare function extractLastErrorLine(stderr: string): string | undefined;
|
|
17
|
+
export declare function installStderrCapture(): () => string;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
export function getFrictionLogPath(envValue, cwd = process.cwd()) {
|
|
4
|
+
if (envValue === undefined)
|
|
5
|
+
return null;
|
|
6
|
+
const trimmed = envValue.trim();
|
|
7
|
+
if (trimmed === '' || trimmed === 'false' || trimmed === '0')
|
|
8
|
+
return null;
|
|
9
|
+
if (trimmed === 'true' || trimmed === '1') {
|
|
10
|
+
return path.join(cwd, '.qfg-friction.log');
|
|
11
|
+
}
|
|
12
|
+
return path.isAbsolute(trimmed) ? trimmed : path.resolve(cwd, trimmed);
|
|
13
|
+
}
|
|
14
|
+
export function buildFrictionEntry(opts) {
|
|
15
|
+
const argv = opts.argv ?? process.argv.slice(2);
|
|
16
|
+
const binName = opts.binName ?? 'qfg';
|
|
17
|
+
const error = opts.error?.trim() || `nonzero exit code ${opts.exitCode}`;
|
|
18
|
+
return {
|
|
19
|
+
attempted: [binName, ...argv].join(' '),
|
|
20
|
+
error,
|
|
21
|
+
exitCode: opts.exitCode,
|
|
22
|
+
ts: (opts.now ?? new Date()).toISOString(),
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export function appendFrictionEntry(logPath, entry) {
|
|
26
|
+
fs.appendFileSync(logPath, JSON.stringify(entry) + '\n', 'utf8');
|
|
27
|
+
}
|
|
28
|
+
const MAX_STDERR_CAPTURE = 4000;
|
|
29
|
+
export function extractLastErrorLine(stderr) {
|
|
30
|
+
if (!stderr)
|
|
31
|
+
return undefined;
|
|
32
|
+
const stripped = stderr.replaceAll(/\u001B\[[\d;]*[A-Za-z]/g, '');
|
|
33
|
+
const lines = stripped.split('\n').map((l) => l.trim()).filter(Boolean);
|
|
34
|
+
if (lines.length === 0)
|
|
35
|
+
return undefined;
|
|
36
|
+
const errorLine = [...lines].reverse().find((l) => /^(error|»|×|✖)/i.test(l));
|
|
37
|
+
return (errorLine ?? lines.at(-1))?.replace(/^»\s*/, '').replace(/^error:\s*/i, '');
|
|
38
|
+
}
|
|
39
|
+
export function installStderrCapture() {
|
|
40
|
+
let captured = '';
|
|
41
|
+
const origWrite = process.stderr.write.bind(process.stderr);
|
|
42
|
+
process.stderr.write = ((chunk, ...rest) => {
|
|
43
|
+
try {
|
|
44
|
+
const asString = typeof chunk === 'string' ? chunk : chunk.toString();
|
|
45
|
+
captured += asString;
|
|
46
|
+
if (captured.length > MAX_STDERR_CAPTURE) {
|
|
47
|
+
captured = captured.slice(-MAX_STDERR_CAPTURE);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
// ignore — stderr capture is best-effort
|
|
52
|
+
}
|
|
53
|
+
return origWrite(chunk, ...rest);
|
|
54
|
+
});
|
|
55
|
+
return () => captured;
|
|
56
|
+
}
|
|
@@ -4,8 +4,8 @@ const WORKOS_BASE_URL = 'https://api.workos.com';
|
|
|
4
4
|
const WORKOS_CLIENT_ID_PRODUCTION = 'client_01KKCCWMNB2K8HYQZQ96G1MXE0';
|
|
5
5
|
const WORKOS_CLIENT_ID_STAGING = 'client_01KKCCWM4K87JS0MKS8PHZ0ZY1';
|
|
6
6
|
const getClientId = () => {
|
|
7
|
-
if (process.env.
|
|
8
|
-
return process.env.
|
|
7
|
+
if (process.env.CLI_WORKOS_CLIENT_ID) {
|
|
8
|
+
return process.env.CLI_WORKOS_CLIENT_ID;
|
|
9
9
|
}
|
|
10
10
|
const domain = getDomain();
|
|
11
11
|
if (domain === 'quonfig-staging.com') {
|
package/dist/verify/validate.js
CHANGED
|
@@ -142,7 +142,7 @@ const StoredConfigSchema = z.object({
|
|
|
142
142
|
valueType: ValueTypeSchema,
|
|
143
143
|
name: z.string().optional(),
|
|
144
144
|
description: z.string().optional(),
|
|
145
|
-
sendToClientSdk: z.boolean().
|
|
145
|
+
sendToClientSdk: z.boolean().optional(),
|
|
146
146
|
schemaKey: z.string().optional(),
|
|
147
147
|
accessLevel: z.string().optional(),
|
|
148
148
|
protection: z.string().optional(),
|
|
@@ -183,6 +183,24 @@ const VALUE_REQUIRED_OPERATORS = new Set([
|
|
|
183
183
|
"IN_INT_RANGE",
|
|
184
184
|
"LOOKUP_KEY_IN", "LOOKUP_KEY_NOT_IN",
|
|
185
185
|
]);
|
|
186
|
+
/**
|
|
187
|
+
* Reject `sendToClientSdk` on feature_flag rows — the field is config-only.
|
|
188
|
+
* Checks the raw parsed JSON so the key is forbidden regardless of value
|
|
189
|
+
* (true or false). Segments are handled separately on the parsed config.
|
|
190
|
+
*/
|
|
191
|
+
function checkFeatureFlagForbiddenFields(parsed, file, issues) {
|
|
192
|
+
if (typeof parsed === "object" &&
|
|
193
|
+
parsed !== null &&
|
|
194
|
+
parsed.type === "feature_flag" &&
|
|
195
|
+
Object.prototype.hasOwnProperty.call(parsed, "sendToClientSdk")) {
|
|
196
|
+
issues.push({
|
|
197
|
+
file,
|
|
198
|
+
message: `feature_flag must not set "sendToClientSdk" — this field is only valid on config rows`,
|
|
199
|
+
severity: "error",
|
|
200
|
+
suggestion: `Remove the "sendToClientSdk" field from this feature_flag JSON`,
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
}
|
|
186
204
|
// ── Validation from filesystem ──────────────────────────────────────────
|
|
187
205
|
import * as fs from "node:fs";
|
|
188
206
|
import * as path from "node:path";
|
|
@@ -334,6 +352,7 @@ export function validateWorkspace(workspaceDir) {
|
|
|
334
352
|
stats.schemas++;
|
|
335
353
|
continue;
|
|
336
354
|
}
|
|
355
|
+
checkFeatureFlagForbiddenFields(parsed, relPath, issues);
|
|
337
356
|
// Validate against StoredConfigSchema
|
|
338
357
|
const result = StoredConfigSchema.safeParse(parsed);
|
|
339
358
|
if (!result.success) {
|
|
@@ -631,6 +650,7 @@ export function validateFileMap(files) {
|
|
|
631
650
|
allSchemaFiles.push({ key: schemaKey, file: relPath });
|
|
632
651
|
continue;
|
|
633
652
|
}
|
|
653
|
+
checkFeatureFlagForbiddenFields(parsed, relPath, issues);
|
|
634
654
|
const result = StoredConfigSchema.safeParse(parsed);
|
|
635
655
|
if (!result.success) {
|
|
636
656
|
for (const issue of result.error.issues) {
|
|
@@ -835,6 +855,19 @@ function validateRules(rules, file, context, expectedValueType, issues, variants
|
|
|
835
855
|
if (wv.weightedValues.length === 0) {
|
|
836
856
|
issues.push({ file, message: `${ruleCtx}: weighted values list is empty`, severity: "error" });
|
|
837
857
|
}
|
|
858
|
+
// Percentage rollouts must reference named variants — the UI cannot edit
|
|
859
|
+
// a rollout when variants is empty. Bool configs are exempt because the
|
|
860
|
+
// UI supplies implicit true/false variants.
|
|
861
|
+
if (wv.weightedValues.length > 0 &&
|
|
862
|
+
variants.length === 0 &&
|
|
863
|
+
expectedValueType !== "bool") {
|
|
864
|
+
issues.push({
|
|
865
|
+
file,
|
|
866
|
+
message: `${ruleCtx}: weighted_values rollout requires at least one variant, but variants is empty`,
|
|
867
|
+
severity: "error",
|
|
868
|
+
suggestion: `Define variants for the possible rollout values, then reference them from weightedValues.`,
|
|
869
|
+
});
|
|
870
|
+
}
|
|
838
871
|
for (let j = 0; j < wv.weightedValues.length; j++) {
|
|
839
872
|
if (wv.weightedValues[j].value.type !== expectedValueType) {
|
|
840
873
|
issues.push({
|
package/oclif.manifest.json
CHANGED
|
@@ -421,7 +421,10 @@
|
|
|
421
421
|
]
|
|
422
422
|
},
|
|
423
423
|
"info": {
|
|
424
|
-
"aliases": [
|
|
424
|
+
"aliases": [
|
|
425
|
+
"flag:show",
|
|
426
|
+
"flag:info"
|
|
427
|
+
],
|
|
425
428
|
"args": {
|
|
426
429
|
"name": {
|
|
427
430
|
"description": "config/feature-flag/etc. name",
|
|
@@ -634,7 +637,9 @@
|
|
|
634
637
|
]
|
|
635
638
|
},
|
|
636
639
|
"list": {
|
|
637
|
-
"aliases": [
|
|
640
|
+
"aliases": [
|
|
641
|
+
"flag:list"
|
|
642
|
+
],
|
|
638
643
|
"args": {},
|
|
639
644
|
"description": "Show keys for your config/feature flags/etc.\n\n All types are returned by default. If you pass one or more type flags (e.g. --configs), only those types will be returned",
|
|
640
645
|
"examples": [
|
|
@@ -1315,7 +1320,9 @@
|
|
|
1315
1320
|
]
|
|
1316
1321
|
},
|
|
1317
1322
|
"set-default": {
|
|
1318
|
-
"aliases": [
|
|
1323
|
+
"aliases": [
|
|
1324
|
+
"toggle"
|
|
1325
|
+
],
|
|
1319
1326
|
"args": {
|
|
1320
1327
|
"name": {
|
|
1321
1328
|
"description": "config/feature-flag/etc. name",
|
|
@@ -2210,5 +2217,5 @@
|
|
|
2210
2217
|
]
|
|
2211
2218
|
}
|
|
2212
2219
|
},
|
|
2213
|
-
"version": "0.0.
|
|
2220
|
+
"version": "0.0.10"
|
|
2214
2221
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"packageManager": "yarn@4.11.0+sha512.4e54aeace9141df2f0177c266b05ec50dc044638157dae128c471ba65994ac802122d7ab35bcd9e81641228b7dcf24867d28e750e0bcae8a05277d600008ad54",
|
|
3
3
|
"name": "@quonfig/cli",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.10",
|
|
5
5
|
"bugs": {
|
|
6
6
|
"url": "https://github.com/quonfig/cli/issues"
|
|
7
7
|
},
|
|
@@ -81,7 +81,10 @@
|
|
|
81
81
|
"plugins": [],
|
|
82
82
|
"topicSeparator": " ",
|
|
83
83
|
"helpClass": "./dist/help",
|
|
84
|
-
"stacktrace": false
|
|
84
|
+
"stacktrace": false,
|
|
85
|
+
"hooks": {
|
|
86
|
+
"init": "./dist/hooks/init/friction-log"
|
|
87
|
+
}
|
|
85
88
|
},
|
|
86
89
|
"scripts": {
|
|
87
90
|
"build": "shx rm -rf dist && tsc -b",
|