@quonfig/cli 0.0.23 → 0.0.24
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 +36 -36
- package/dist/commands/log-level.js +1 -3
- package/dist/commands/login.js +30 -1
- package/dist/commands/workspace/switch.js +1 -4
- package/dist/migrate/push-to-cloud.js +3 -6
- package/dist/migrate/sources/launch/zod-to-json-schema.js +130 -132
- package/dist/migrate/sources/launch.js +1 -1
- package/dist/util/open-browser.d.ts +1 -0
- package/dist/util/open-browser.js +27 -0
- package/dist/util/resolve-workspace.js +6 -2
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
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.24 linux-x64 node-v24.4.1
|
|
18
18
|
$ qfg --help [COMMAND]
|
|
19
19
|
USAGE
|
|
20
20
|
$ qfg COMMAND
|
|
@@ -92,7 +92,7 @@ EXAMPLES
|
|
|
92
92
|
$ qfg config-schema --json-schema # full JSON Schema document (copy into your editor)
|
|
93
93
|
```
|
|
94
94
|
|
|
95
|
-
_See code: [src/commands/config-schema.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
95
|
+
_See code: [src/commands/config-schema.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/config-schema.ts)_
|
|
96
96
|
|
|
97
97
|
## `qfg create NAME`
|
|
98
98
|
|
|
@@ -177,7 +177,7 @@ EXAMPLES
|
|
|
177
177
|
$ qfg set-rollout my.new.flag --environment production --true-percent 20
|
|
178
178
|
```
|
|
179
179
|
|
|
180
|
-
_See code: [src/commands/create.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
180
|
+
_See code: [src/commands/create.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/create.ts)_
|
|
181
181
|
|
|
182
182
|
## `qfg delete NAME`
|
|
183
183
|
|
|
@@ -223,7 +223,7 @@ EXAMPLES
|
|
|
223
223
|
$ qfg delete my.config --yes
|
|
224
224
|
```
|
|
225
225
|
|
|
226
|
-
_See code: [src/commands/delete.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
226
|
+
_See code: [src/commands/delete.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/delete.ts)_
|
|
227
227
|
|
|
228
228
|
## `qfg flag info [NAME]`
|
|
229
229
|
|
|
@@ -426,7 +426,7 @@ EXAMPLES
|
|
|
426
426
|
$ qfg generate --targets node-ts -o ./dist # combine with targets
|
|
427
427
|
```
|
|
428
428
|
|
|
429
|
-
_See code: [src/commands/generate.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
429
|
+
_See code: [src/commands/generate.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/generate.ts)_
|
|
430
430
|
|
|
431
431
|
## `qfg generate-new-hex-key`
|
|
432
432
|
|
|
@@ -449,7 +449,7 @@ EXAMPLES
|
|
|
449
449
|
$ qfg generate-new-hex-key
|
|
450
450
|
```
|
|
451
451
|
|
|
452
|
-
_See code: [src/commands/generate-new-hex-key.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
452
|
+
_See code: [src/commands/generate-new-hex-key.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/generate-new-hex-key.ts)_
|
|
453
453
|
|
|
454
454
|
## `qfg get [NAME]`
|
|
455
455
|
|
|
@@ -481,7 +481,7 @@ EXAMPLES
|
|
|
481
481
|
$ qfg get my.config.name --environment=production
|
|
482
482
|
```
|
|
483
483
|
|
|
484
|
-
_See code: [src/commands/get.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
484
|
+
_See code: [src/commands/get.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/get.ts)_
|
|
485
485
|
|
|
486
486
|
## `qfg info [NAME]`
|
|
487
487
|
|
|
@@ -529,7 +529,7 @@ EXAMPLES
|
|
|
529
529
|
$ qfg info my.config.name --exclude-evaluations # skip 24h stats
|
|
530
530
|
```
|
|
531
531
|
|
|
532
|
-
_See code: [src/commands/info.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
532
|
+
_See code: [src/commands/info.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/info.ts)_
|
|
533
533
|
|
|
534
534
|
## `qfg init [DIRECTORY]`
|
|
535
535
|
|
|
@@ -569,7 +569,7 @@ EXAMPLES
|
|
|
569
569
|
$ qfg init --dry-run
|
|
570
570
|
```
|
|
571
571
|
|
|
572
|
-
_See code: [src/commands/init.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
572
|
+
_See code: [src/commands/init.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/init.ts)_
|
|
573
573
|
|
|
574
574
|
## `qfg interactive`
|
|
575
575
|
|
|
@@ -599,7 +599,7 @@ EXAMPLES
|
|
|
599
599
|
$ qfg
|
|
600
600
|
```
|
|
601
601
|
|
|
602
|
-
_See code: [src/commands/interactive.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
602
|
+
_See code: [src/commands/interactive.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/interactive.ts)_
|
|
603
603
|
|
|
604
604
|
## `qfg list`
|
|
605
605
|
|
|
@@ -639,7 +639,7 @@ EXAMPLES
|
|
|
639
639
|
$ qfg list --feature-flags
|
|
640
640
|
```
|
|
641
641
|
|
|
642
|
-
_See code: [src/commands/list.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
642
|
+
_See code: [src/commands/list.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/list.ts)_
|
|
643
643
|
|
|
644
644
|
## `qfg log-level NAME`
|
|
645
645
|
|
|
@@ -704,7 +704,7 @@ EXAMPLES
|
|
|
704
704
|
$ qfg log-level log-level.my-app --target=Chatty --value=INFO --environment=production
|
|
705
705
|
```
|
|
706
706
|
|
|
707
|
-
_See code: [src/commands/log-level.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
707
|
+
_See code: [src/commands/log-level.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/log-level.ts)_
|
|
708
708
|
|
|
709
709
|
## `qfg login`
|
|
710
710
|
|
|
@@ -729,7 +729,7 @@ EXAMPLES
|
|
|
729
729
|
$ qfg login --profile myprofile
|
|
730
730
|
```
|
|
731
731
|
|
|
732
|
-
_See code: [src/commands/login.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
732
|
+
_See code: [src/commands/login.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/login.ts)_
|
|
733
733
|
|
|
734
734
|
## `qfg logout`
|
|
735
735
|
|
|
@@ -752,7 +752,7 @@ EXAMPLES
|
|
|
752
752
|
$ qfg logout
|
|
753
753
|
```
|
|
754
754
|
|
|
755
|
-
_See code: [src/commands/logout.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
755
|
+
_See code: [src/commands/logout.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/logout.ts)_
|
|
756
756
|
|
|
757
757
|
## `qfg mcp`
|
|
758
758
|
|
|
@@ -785,7 +785,7 @@ EXAMPLES
|
|
|
785
785
|
$ qfg mcp --url http://local-app.quonfig-staging.com:3003/api/v1/mcp
|
|
786
786
|
```
|
|
787
787
|
|
|
788
|
-
_See code: [src/commands/mcp.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
788
|
+
_See code: [src/commands/mcp.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/mcp.ts)_
|
|
789
789
|
|
|
790
790
|
## `qfg migrate`
|
|
791
791
|
|
|
@@ -839,7 +839,7 @@ EXAMPLES
|
|
|
839
839
|
$ qfg migrate --from launch --api-key $LAUNCH_API_KEY --dir ./quonfig-config --staging
|
|
840
840
|
```
|
|
841
841
|
|
|
842
|
-
_See code: [src/commands/migrate.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
842
|
+
_See code: [src/commands/migrate.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/migrate.ts)_
|
|
843
843
|
|
|
844
844
|
## `qfg migrate doctor`
|
|
845
845
|
|
|
@@ -877,7 +877,7 @@ EXAMPLES
|
|
|
877
877
|
$ qfg migrate doctor --json
|
|
878
878
|
```
|
|
879
879
|
|
|
880
|
-
_See code: [src/commands/migrate/doctor.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
880
|
+
_See code: [src/commands/migrate/doctor.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/migrate/doctor.ts)_
|
|
881
881
|
|
|
882
882
|
## `qfg migrate my-code`
|
|
883
883
|
|
|
@@ -909,7 +909,7 @@ EXAMPLES
|
|
|
909
909
|
$ qfg migrate my-code --dry-run
|
|
910
910
|
```
|
|
911
911
|
|
|
912
|
-
_See code: [src/commands/migrate/my-code.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
912
|
+
_See code: [src/commands/migrate/my-code.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/migrate/my-code.ts)_
|
|
913
913
|
|
|
914
914
|
## `qfg migrate status`
|
|
915
915
|
|
|
@@ -940,7 +940,7 @@ EXAMPLES
|
|
|
940
940
|
$ qfg migrate status --json
|
|
941
941
|
```
|
|
942
942
|
|
|
943
|
-
_See code: [src/commands/migrate/status.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
943
|
+
_See code: [src/commands/migrate/status.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/migrate/status.ts)_
|
|
944
944
|
|
|
945
945
|
## `qfg override [NAME] [VALUE]`
|
|
946
946
|
|
|
@@ -995,7 +995,7 @@ EXAMPLES
|
|
|
995
995
|
$ qfg override my.flag true --env=staging
|
|
996
996
|
```
|
|
997
997
|
|
|
998
|
-
_See code: [src/commands/override.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
998
|
+
_See code: [src/commands/override.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/override.ts)_
|
|
999
999
|
|
|
1000
1000
|
## `qfg pull`
|
|
1001
1001
|
|
|
@@ -1039,7 +1039,7 @@ EXAMPLES
|
|
|
1039
1039
|
$ qfg pull # uses QUONFIG_DIR env var
|
|
1040
1040
|
```
|
|
1041
1041
|
|
|
1042
|
-
_See code: [src/commands/pull.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1042
|
+
_See code: [src/commands/pull.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/pull.ts)_
|
|
1043
1043
|
|
|
1044
1044
|
## `qfg push`
|
|
1045
1045
|
|
|
@@ -1087,7 +1087,7 @@ EXAMPLES
|
|
|
1087
1087
|
$ qfg push --dir ./our-config --yes
|
|
1088
1088
|
```
|
|
1089
1089
|
|
|
1090
|
-
_See code: [src/commands/push.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1090
|
+
_See code: [src/commands/push.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/push.ts)_
|
|
1091
1091
|
|
|
1092
1092
|
## `qfg schema NAME`
|
|
1093
1093
|
|
|
@@ -1125,7 +1125,7 @@ EXAMPLES
|
|
|
1125
1125
|
$ qfg schema my-schema --set-json-schema=@schemas/my-schema.json --protected
|
|
1126
1126
|
```
|
|
1127
1127
|
|
|
1128
|
-
_See code: [src/commands/schema.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1128
|
+
_See code: [src/commands/schema.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/schema.ts)_
|
|
1129
1129
|
|
|
1130
1130
|
## `qfg sdk-key`
|
|
1131
1131
|
|
|
@@ -1152,7 +1152,7 @@ EXAMPLES
|
|
|
1152
1152
|
$ qfg sdk-key revoke <key-id>
|
|
1153
1153
|
```
|
|
1154
1154
|
|
|
1155
|
-
_See code: [src/commands/sdk-key.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1155
|
+
_See code: [src/commands/sdk-key.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/sdk-key.ts)_
|
|
1156
1156
|
|
|
1157
1157
|
## `qfg sdk-key create`
|
|
1158
1158
|
|
|
@@ -1184,7 +1184,7 @@ EXAMPLES
|
|
|
1184
1184
|
$ qfg sdk-key create --environment staging --type browser
|
|
1185
1185
|
```
|
|
1186
1186
|
|
|
1187
|
-
_See code: [src/commands/sdk-key/create.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1187
|
+
_See code: [src/commands/sdk-key/create.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/sdk-key/create.ts)_
|
|
1188
1188
|
|
|
1189
1189
|
## `qfg sdk-key list`
|
|
1190
1190
|
|
|
@@ -1213,7 +1213,7 @@ EXAMPLES
|
|
|
1213
1213
|
$ qfg sdk-key list --environment production
|
|
1214
1214
|
```
|
|
1215
1215
|
|
|
1216
|
-
_See code: [src/commands/sdk-key/list.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1216
|
+
_See code: [src/commands/sdk-key/list.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/sdk-key/list.ts)_
|
|
1217
1217
|
|
|
1218
1218
|
## `qfg sdk-key revoke KEYID`
|
|
1219
1219
|
|
|
@@ -1240,7 +1240,7 @@ EXAMPLES
|
|
|
1240
1240
|
$ qfg sdk-key revoke a1b2c3d4-e5f6-7890-abcd-ef1234567890
|
|
1241
1241
|
```
|
|
1242
1242
|
|
|
1243
|
-
_See code: [src/commands/sdk-key/revoke.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1243
|
+
_See code: [src/commands/sdk-key/revoke.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/sdk-key/revoke.ts)_
|
|
1244
1244
|
|
|
1245
1245
|
## `qfg set-default [NAME]`
|
|
1246
1246
|
|
|
@@ -1311,7 +1311,7 @@ EXAMPLES
|
|
|
1311
1311
|
$ qfg pull && qfg config-schema
|
|
1312
1312
|
```
|
|
1313
1313
|
|
|
1314
|
-
_See code: [src/commands/set-default.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1314
|
+
_See code: [src/commands/set-default.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/set-default.ts)_
|
|
1315
1315
|
|
|
1316
1316
|
## `qfg set-rollout [NAME]`
|
|
1317
1317
|
|
|
@@ -1388,7 +1388,7 @@ EXAMPLES
|
|
|
1388
1388
|
$ qfg set-rollout my.variant.flag --environment production --weights "a:33,b:33,c:34" --hash-by user.id
|
|
1389
1389
|
```
|
|
1390
1390
|
|
|
1391
|
-
_See code: [src/commands/set-rollout.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1391
|
+
_See code: [src/commands/set-rollout.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/set-rollout.ts)_
|
|
1392
1392
|
|
|
1393
1393
|
## `qfg sync`
|
|
1394
1394
|
|
|
@@ -1420,7 +1420,7 @@ EXAMPLES
|
|
|
1420
1420
|
$ qfg sync --watch --dir ./our-config --interval 30
|
|
1421
1421
|
```
|
|
1422
1422
|
|
|
1423
|
-
_See code: [src/commands/sync.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1423
|
+
_See code: [src/commands/sync.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/sync.ts)_
|
|
1424
1424
|
|
|
1425
1425
|
## `qfg toggle [NAME]`
|
|
1426
1426
|
|
|
@@ -1522,7 +1522,7 @@ EXAMPLES
|
|
|
1522
1522
|
$ qfg verify --json
|
|
1523
1523
|
```
|
|
1524
1524
|
|
|
1525
|
-
_See code: [src/commands/verify.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1525
|
+
_See code: [src/commands/verify.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/verify.ts)_
|
|
1526
1526
|
|
|
1527
1527
|
## `qfg whoami`
|
|
1528
1528
|
|
|
@@ -1545,7 +1545,7 @@ EXAMPLES
|
|
|
1545
1545
|
$ qfg whoami
|
|
1546
1546
|
```
|
|
1547
1547
|
|
|
1548
|
-
_See code: [src/commands/whoami.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1548
|
+
_See code: [src/commands/whoami.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/whoami.ts)_
|
|
1549
1549
|
|
|
1550
1550
|
## `qfg workspace`
|
|
1551
1551
|
|
|
@@ -1568,7 +1568,7 @@ EXAMPLES
|
|
|
1568
1568
|
$ qfg workspace
|
|
1569
1569
|
```
|
|
1570
1570
|
|
|
1571
|
-
_See code: [src/commands/workspace.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1571
|
+
_See code: [src/commands/workspace.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/workspace.ts)_
|
|
1572
1572
|
|
|
1573
1573
|
## `qfg workspace bootstrap`
|
|
1574
1574
|
|
|
@@ -1601,7 +1601,7 @@ EXAMPLES
|
|
|
1601
1601
|
$ qfg workspace bootstrap --dir ./our-config --skip-validate
|
|
1602
1602
|
```
|
|
1603
1603
|
|
|
1604
|
-
_See code: [src/commands/workspace/bootstrap.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1604
|
+
_See code: [src/commands/workspace/bootstrap.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/workspace/bootstrap.ts)_
|
|
1605
1605
|
|
|
1606
1606
|
## `qfg workspace create SLUG`
|
|
1607
1607
|
|
|
@@ -1635,7 +1635,7 @@ EXAMPLES
|
|
|
1635
1635
|
$ qfg workspace create my-team --org 11111111-1111-1111-1111-111111111111
|
|
1636
1636
|
```
|
|
1637
1637
|
|
|
1638
|
-
_See code: [src/commands/workspace/create.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1638
|
+
_See code: [src/commands/workspace/create.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/workspace/create.ts)_
|
|
1639
1639
|
|
|
1640
1640
|
## `qfg workspace switch [SLUG]`
|
|
1641
1641
|
|
|
@@ -1663,7 +1663,7 @@ EXAMPLES
|
|
|
1663
1663
|
$ qfg workspace switch my-workspace-slug
|
|
1664
1664
|
```
|
|
1665
1665
|
|
|
1666
|
-
_See code: [src/commands/workspace/switch.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1666
|
+
_See code: [src/commands/workspace/switch.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/workspace/switch.ts)_
|
|
1667
1667
|
<!-- commandsstop -->
|
|
1668
1668
|
|
|
1669
1669
|
## Local Development
|
|
@@ -122,9 +122,7 @@ For more complex rules (regex, multi-criterion, exact match) edit the JSON:
|
|
|
122
122
|
// Merge semantics: if an existing rule has the same exact targeting
|
|
123
123
|
// criterion (same propertyName, same valueToMatch set), replace its value.
|
|
124
124
|
// Otherwise insert the new rule before the catch-all so it takes priority.
|
|
125
|
-
const existingRules = environment
|
|
126
|
-
? findEnvRules(current, environment)
|
|
127
|
-
: current.default?.rules ?? [];
|
|
125
|
+
const existingRules = environment ? findEnvRules(current, environment) : (current.default?.rules ?? []);
|
|
128
126
|
const mergedRules = mergeTargetingRule(existingRules, newRule);
|
|
129
127
|
const logLevelPatch = environment
|
|
130
128
|
? { environments: replaceEnvRules(current.environments ?? [], environment, mergedRules) }
|
package/dist/commands/login.js
CHANGED
|
@@ -3,6 +3,7 @@ import { select } from '@inquirer/prompts';
|
|
|
3
3
|
import { BaseCommand } from '../index.js';
|
|
4
4
|
import { decodeJWT, pollForToken, requestDeviceCode } from '../util/oauth-client.js';
|
|
5
5
|
import { getApiUrl } from '../util/domain-urls.js';
|
|
6
|
+
import { openBrowser } from '../util/open-browser.js';
|
|
6
7
|
import { getAuthConfigFilePath, getTokenFilePath, loadAuthConfig, saveAuthConfig, saveTokens, } from '../util/token-storage.js';
|
|
7
8
|
export default class Login extends BaseCommand {
|
|
8
9
|
static description = 'Log in to Quonfig via WorkOS device authorization';
|
|
@@ -27,9 +28,37 @@ export default class Login extends BaseCommand {
|
|
|
27
28
|
this.log(`\nTo authenticate, visit:\n`);
|
|
28
29
|
this.log(` ${deviceAuth.verification_uri_complete}\n`);
|
|
29
30
|
this.log(`Or go to ${deviceAuth.verification_uri} and enter code: ${deviceAuth.user_code}\n`);
|
|
31
|
+
// Listen for Enter to open the URL in the user's browser.
|
|
32
|
+
// Runs concurrently with token polling so the user can also authenticate
|
|
33
|
+
// by pasting the URL manually without having to press Enter first.
|
|
34
|
+
const stdinIsTTY = Boolean(process.stdin.isTTY);
|
|
35
|
+
let browserOpened = false;
|
|
36
|
+
const onEnter = (chunk) => {
|
|
37
|
+
if (browserOpened)
|
|
38
|
+
return;
|
|
39
|
+
if (!chunk.toString().match(/[\r\n]/))
|
|
40
|
+
return;
|
|
41
|
+
browserOpened = true;
|
|
42
|
+
openBrowser(deviceAuth.verification_uri_complete);
|
|
43
|
+
this.log('Opening browser...');
|
|
44
|
+
};
|
|
45
|
+
if (stdinIsTTY) {
|
|
46
|
+
this.log('Press Enter to open the URL in your browser, or open it manually.');
|
|
47
|
+
process.stdin.on('data', onEnter);
|
|
48
|
+
process.stdin.resume();
|
|
49
|
+
}
|
|
30
50
|
this.log('Waiting for authentication...');
|
|
31
51
|
// Step 3: Poll for token
|
|
32
|
-
|
|
52
|
+
let tokenResponse;
|
|
53
|
+
try {
|
|
54
|
+
tokenResponse = await pollForToken(deviceAuth.device_code, deviceAuth.interval, deviceAuth.expires_in, this.isVerbose);
|
|
55
|
+
}
|
|
56
|
+
finally {
|
|
57
|
+
if (stdinIsTTY) {
|
|
58
|
+
process.stdin.off('data', onEnter);
|
|
59
|
+
process.stdin.pause();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
33
62
|
const user = tokenResponse.user;
|
|
34
63
|
const userEmail = user.email;
|
|
35
64
|
// Decode JWT to inspect claims
|
|
@@ -13,10 +13,7 @@ export default class WorkspaceSwitch extends BaseCommand {
|
|
|
13
13
|
}),
|
|
14
14
|
};
|
|
15
15
|
static description = 'Switch to a different workspace';
|
|
16
|
-
static examples = [
|
|
17
|
-
'<%= config.bin %> <%= command.id %>',
|
|
18
|
-
'<%= config.bin %> <%= command.id %> my-workspace-slug',
|
|
19
|
-
];
|
|
16
|
+
static examples = ['<%= config.bin %> <%= command.id %>', '<%= config.bin %> <%= command.id %> my-workspace-slug'];
|
|
20
17
|
async run() {
|
|
21
18
|
const { args } = await this.parse(WorkspaceSwitch);
|
|
22
19
|
// Get a valid token — refresh silently if needed, error if not logged in
|
|
@@ -3,7 +3,7 @@ import * as path from 'node:path';
|
|
|
3
3
|
import { removeQfFromGitignore, writeImportState } from './import-state.js';
|
|
4
4
|
import { writeMigrationReport } from './migration-report.js';
|
|
5
5
|
import { detectDuplicateKeys } from './sources/launch/translate.js';
|
|
6
|
-
import { cloneAndStackPush } from '../util/clone-and-stack-push.js';
|
|
6
|
+
import { cloneAndStackPush, } from '../util/clone-and-stack-push.js';
|
|
7
7
|
const writeQuonfigFiles = (dir, changes, source) => {
|
|
8
8
|
const livePaths = new Map();
|
|
9
9
|
for (const change of changes) {
|
|
@@ -59,8 +59,7 @@ const mergeEnvironmentsIntoQuonfigJson = (dir, sourceEnvs) => {
|
|
|
59
59
|
}
|
|
60
60
|
const existingEnvs = Array.isArray(existing.environments) ? existing.environments : [];
|
|
61
61
|
const merged = [...new Set([...existingEnvs, ...sourceEnvs])].sort();
|
|
62
|
-
if (existingEnvs.length === merged.length &&
|
|
63
|
-
existingEnvs.every((e, i) => e === merged[i])) {
|
|
62
|
+
if (existingEnvs.length === merged.length && existingEnvs.every((e, i) => e === merged[i])) {
|
|
64
63
|
return;
|
|
65
64
|
}
|
|
66
65
|
fs.writeFileSync(quonfigPath, JSON.stringify({ ...existing, environments: merged }, null, 2) + '\n', 'utf8');
|
|
@@ -80,9 +79,7 @@ export const pushMigrationToCloud = async (opts) => {
|
|
|
80
79
|
droppedOverrides = opts.source.getDroppedOverrides?.() ?? null;
|
|
81
80
|
skippedConfigs = opts.source.getSkippedConfigs?.() ?? null;
|
|
82
81
|
duplicateResolutions =
|
|
83
|
-
resolutionEntries.length > 0
|
|
84
|
-
? { entries: resolutionEntries, total: resolutionEntries.length }
|
|
85
|
-
: null;
|
|
82
|
+
resolutionEntries.length > 0 ? { entries: resolutionEntries, total: resolutionEntries.length } : null;
|
|
86
83
|
const reportData = {
|
|
87
84
|
...opts.reportData,
|
|
88
85
|
...(droppedOverrides ? { droppedOverrides } : {}),
|
|
@@ -2,27 +2,25 @@
|
|
|
2
2
|
// Vendored verbatim from launch-migrator/src/zod-to-json-schema.ts to keep a
|
|
3
3
|
// drop-in sync path. Do not reformat — edits here should be mirrored against
|
|
4
4
|
// launch-migrator so the two converters stay behaviorally identical.
|
|
5
|
-
import * as ts from
|
|
6
|
-
const JSON_SCHEMA_DRAFT_2020_12 =
|
|
5
|
+
import * as ts from 'typescript';
|
|
6
|
+
const JSON_SCHEMA_DRAFT_2020_12 = 'https://json-schema.org/draft/2020-12/schema';
|
|
7
7
|
function unwrapExpression(node) {
|
|
8
8
|
let current = node;
|
|
9
|
-
while (ts.isParenthesizedExpression(current) ||
|
|
10
|
-
ts.isAsExpression(current) ||
|
|
11
|
-
ts.isTypeAssertionExpression(current)) {
|
|
9
|
+
while (ts.isParenthesizedExpression(current) || ts.isAsExpression(current) || ts.isTypeAssertionExpression(current)) {
|
|
12
10
|
current = current.expression;
|
|
13
11
|
}
|
|
14
12
|
return current;
|
|
15
13
|
}
|
|
16
14
|
function parseRegexLiteral(text) {
|
|
17
|
-
if (!text.startsWith(
|
|
15
|
+
if (!text.startsWith('/')) {
|
|
18
16
|
throw new Error(`Expected regex literal, got: ${text}`);
|
|
19
17
|
}
|
|
20
18
|
let lastSlash = -1;
|
|
21
19
|
for (let index = text.length - 1; index > 0; index -= 1) {
|
|
22
|
-
if (text[index] !==
|
|
20
|
+
if (text[index] !== '/')
|
|
23
21
|
continue;
|
|
24
22
|
let backslashCount = 0;
|
|
25
|
-
for (let i = index - 1; i >= 0 && text[i] ===
|
|
23
|
+
for (let i = index - 1; i >= 0 && text[i] === '\\'; i -= 1) {
|
|
26
24
|
backslashCount += 1;
|
|
27
25
|
}
|
|
28
26
|
if (backslashCount % 2 === 0) {
|
|
@@ -39,7 +37,7 @@ function parseRegexLiteral(text) {
|
|
|
39
37
|
};
|
|
40
38
|
}
|
|
41
39
|
function isPlainObject(value) {
|
|
42
|
-
return typeof value ===
|
|
40
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
43
41
|
}
|
|
44
42
|
function evaluateLiteralExpression(node) {
|
|
45
43
|
const current = unwrapExpression(node);
|
|
@@ -57,13 +55,13 @@ function evaluateLiteralExpression(node) {
|
|
|
57
55
|
return null;
|
|
58
56
|
if (ts.isPrefixUnaryExpression(current) && current.operator === ts.SyntaxKind.MinusToken) {
|
|
59
57
|
const operand = evaluateLiteralExpression(current.operand);
|
|
60
|
-
if (typeof operand ===
|
|
58
|
+
if (typeof operand === 'number')
|
|
61
59
|
return -operand;
|
|
62
60
|
}
|
|
63
61
|
if (ts.isArrayLiteralExpression(current)) {
|
|
64
62
|
return current.elements.map((element) => {
|
|
65
63
|
if (ts.isSpreadElement(element)) {
|
|
66
|
-
throw new Error(
|
|
64
|
+
throw new Error('Spread elements are not supported in literal arrays');
|
|
67
65
|
}
|
|
68
66
|
return evaluateLiteralExpression(element);
|
|
69
67
|
});
|
|
@@ -72,10 +70,10 @@ function evaluateLiteralExpression(node) {
|
|
|
72
70
|
const result = {};
|
|
73
71
|
for (const property of current.properties) {
|
|
74
72
|
if (ts.isSpreadAssignment(property) || ts.isShorthandPropertyAssignment(property)) {
|
|
75
|
-
throw new Error(
|
|
73
|
+
throw new Error('Spread and shorthand properties are not supported in literal objects');
|
|
76
74
|
}
|
|
77
75
|
if (!ts.isPropertyAssignment(property)) {
|
|
78
|
-
throw new Error(
|
|
76
|
+
throw new Error('Unsupported object literal property');
|
|
79
77
|
}
|
|
80
78
|
const key = getPropertyName(property.name);
|
|
81
79
|
result[key] = evaluateLiteralExpression(property.initializer);
|
|
@@ -88,14 +86,14 @@ function evaluateLiteralExpression(node) {
|
|
|
88
86
|
if (ts.isBinaryExpression(current) && current.operatorToken.kind === ts.SyntaxKind.PlusToken) {
|
|
89
87
|
const left = evaluateLiteralExpression(current.left);
|
|
90
88
|
const right = evaluateLiteralExpression(current.right);
|
|
91
|
-
if (typeof left ===
|
|
89
|
+
if (typeof left === 'string' || typeof right === 'string') {
|
|
92
90
|
return String(left) + String(right);
|
|
93
91
|
}
|
|
94
|
-
if (typeof left ===
|
|
92
|
+
if (typeof left === 'number' && typeof right === 'number') {
|
|
95
93
|
return left + right;
|
|
96
94
|
}
|
|
97
95
|
}
|
|
98
|
-
if (ts.isIdentifier(current) && current.text ===
|
|
96
|
+
if (ts.isIdentifier(current) && current.text === 'undefined') {
|
|
99
97
|
return undefined;
|
|
100
98
|
}
|
|
101
99
|
throw new Error(`Unsupported literal expression: ${current.getText()}`);
|
|
@@ -108,38 +106,38 @@ function getPropertyName(name) {
|
|
|
108
106
|
}
|
|
109
107
|
function schemaKind(schema) {
|
|
110
108
|
if (schema === false)
|
|
111
|
-
return
|
|
109
|
+
return 'never';
|
|
112
110
|
if (!isPlainObject(schema))
|
|
113
|
-
return
|
|
111
|
+
return 'unknown';
|
|
114
112
|
if (Array.isArray(schema.anyOf))
|
|
115
|
-
return
|
|
113
|
+
return 'union';
|
|
116
114
|
if (schema.const !== undefined)
|
|
117
|
-
return
|
|
115
|
+
return 'const';
|
|
118
116
|
if (Array.isArray(schema.enum))
|
|
119
|
-
return
|
|
117
|
+
return 'enum';
|
|
120
118
|
const type = schema.type;
|
|
121
|
-
if (typeof type ===
|
|
119
|
+
if (typeof type === 'string')
|
|
122
120
|
return type;
|
|
123
|
-
return
|
|
121
|
+
return 'unknown';
|
|
124
122
|
}
|
|
125
123
|
function ensureObjectSchema(schema) {
|
|
126
|
-
if (schema === false || !isPlainObject(schema) || schema.type !==
|
|
127
|
-
throw new Error(
|
|
124
|
+
if (schema === false || !isPlainObject(schema) || schema.type !== 'object') {
|
|
125
|
+
throw new Error('Expected object schema');
|
|
128
126
|
}
|
|
129
127
|
return schema;
|
|
130
128
|
}
|
|
131
129
|
function makeNullable(schema) {
|
|
132
130
|
if (!isPlainObject(schema)) {
|
|
133
|
-
return { anyOf: [schema, { type:
|
|
131
|
+
return { anyOf: [schema, { type: 'null' }] };
|
|
134
132
|
}
|
|
135
133
|
const existing = schema.type;
|
|
136
134
|
if (Array.isArray(existing)) {
|
|
137
|
-
return existing.includes(
|
|
135
|
+
return existing.includes('null') ? schema : { ...schema, type: [...existing, 'null'] };
|
|
138
136
|
}
|
|
139
|
-
if (typeof existing ===
|
|
140
|
-
return { ...schema, type: [existing,
|
|
137
|
+
if (typeof existing === 'string') {
|
|
138
|
+
return { ...schema, type: [existing, 'null'] };
|
|
141
139
|
}
|
|
142
|
-
return { anyOf: [schema, { type:
|
|
140
|
+
return { anyOf: [schema, { type: 'null' }] };
|
|
143
141
|
}
|
|
144
142
|
function withAnnotation(schema, key, value) {
|
|
145
143
|
if (schema === false) {
|
|
@@ -153,24 +151,24 @@ function applyNumericConstraint(schema, kind, method, value) {
|
|
|
153
151
|
if (schema === false)
|
|
154
152
|
return schema;
|
|
155
153
|
const next = { ...schema };
|
|
156
|
-
if (kind ===
|
|
157
|
-
if (method ===
|
|
154
|
+
if (kind === 'number' || kind === 'integer') {
|
|
155
|
+
if (method === 'min' || method === 'length')
|
|
158
156
|
next.minimum = value;
|
|
159
|
-
if (method ===
|
|
157
|
+
if (method === 'max' || method === 'length')
|
|
160
158
|
next.maximum = value;
|
|
161
159
|
return next;
|
|
162
160
|
}
|
|
163
|
-
if (kind ===
|
|
164
|
-
if (method ===
|
|
161
|
+
if (kind === 'string') {
|
|
162
|
+
if (method === 'min' || method === 'length')
|
|
165
163
|
next.minLength = value;
|
|
166
|
-
if (method ===
|
|
164
|
+
if (method === 'max' || method === 'length')
|
|
167
165
|
next.maxLength = value;
|
|
168
166
|
return next;
|
|
169
167
|
}
|
|
170
|
-
if (kind ===
|
|
171
|
-
if (method ===
|
|
168
|
+
if (kind === 'array') {
|
|
169
|
+
if (method === 'min' || method === 'length')
|
|
172
170
|
next.minItems = value;
|
|
173
|
-
if (method ===
|
|
171
|
+
if (method === 'max' || method === 'length')
|
|
174
172
|
next.maxItems = value;
|
|
175
173
|
return next;
|
|
176
174
|
}
|
|
@@ -179,35 +177,35 @@ function applyNumericConstraint(schema, kind, method, value) {
|
|
|
179
177
|
function applyMethod(receiver, method, args, ctx) {
|
|
180
178
|
const kind = schemaKind(receiver.schema);
|
|
181
179
|
switch (method) {
|
|
182
|
-
case
|
|
180
|
+
case 'optional':
|
|
183
181
|
return { ...receiver, optional: true };
|
|
184
|
-
case
|
|
182
|
+
case 'nullable':
|
|
185
183
|
return { schema: makeNullable(receiver.schema), optional: receiver.optional };
|
|
186
|
-
case
|
|
184
|
+
case 'default': {
|
|
187
185
|
if (args.length === 0)
|
|
188
|
-
throw new Error(
|
|
186
|
+
throw new Error('default() requires an argument');
|
|
189
187
|
return {
|
|
190
|
-
schema: withAnnotation(receiver.schema,
|
|
188
|
+
schema: withAnnotation(receiver.schema, 'default', evaluateLiteralExpression(args[0])),
|
|
191
189
|
optional: true,
|
|
192
190
|
};
|
|
193
191
|
}
|
|
194
|
-
case
|
|
192
|
+
case 'describe': {
|
|
195
193
|
if (args.length === 0)
|
|
196
|
-
throw new Error(
|
|
194
|
+
throw new Error('describe() requires an argument');
|
|
197
195
|
return {
|
|
198
|
-
schema: withAnnotation(receiver.schema,
|
|
196
|
+
schema: withAnnotation(receiver.schema, 'description', evaluateLiteralExpression(args[0])),
|
|
199
197
|
optional: receiver.optional,
|
|
200
198
|
};
|
|
201
199
|
}
|
|
202
|
-
case
|
|
200
|
+
case 'meta': {
|
|
203
201
|
if (args.length === 0)
|
|
204
|
-
throw new Error(
|
|
202
|
+
throw new Error('meta() requires an argument');
|
|
205
203
|
const meta = evaluateLiteralExpression(args[0]);
|
|
206
204
|
if (!isPlainObject(meta)) {
|
|
207
|
-
throw new Error(
|
|
205
|
+
throw new Error('meta() expects an object literal');
|
|
208
206
|
}
|
|
209
207
|
let schema = receiver.schema;
|
|
210
|
-
const supportedKeys = new Set([
|
|
208
|
+
const supportedKeys = new Set(['description', 'title']);
|
|
211
209
|
for (const [key, value] of Object.entries(meta)) {
|
|
212
210
|
if (supportedKeys.has(key)) {
|
|
213
211
|
schema = withAnnotation(schema, key, value);
|
|
@@ -218,99 +216,99 @@ function applyMethod(receiver, method, args, ctx) {
|
|
|
218
216
|
}
|
|
219
217
|
return { schema, optional: receiver.optional };
|
|
220
218
|
}
|
|
221
|
-
case
|
|
222
|
-
if (kind !==
|
|
223
|
-
throw new Error(
|
|
219
|
+
case 'url':
|
|
220
|
+
if (kind !== 'string') {
|
|
221
|
+
throw new Error('url() can only be applied to string schemas');
|
|
224
222
|
}
|
|
225
223
|
return {
|
|
226
|
-
schema: withAnnotation(receiver.schema,
|
|
224
|
+
schema: withAnnotation(receiver.schema, 'format', 'uri'),
|
|
227
225
|
optional: receiver.optional,
|
|
228
226
|
};
|
|
229
|
-
case
|
|
230
|
-
if (kind !==
|
|
231
|
-
throw new Error(
|
|
227
|
+
case 'email':
|
|
228
|
+
if (kind !== 'string') {
|
|
229
|
+
throw new Error('email() can only be applied to string schemas');
|
|
232
230
|
}
|
|
233
231
|
return {
|
|
234
|
-
schema: withAnnotation(receiver.schema,
|
|
232
|
+
schema: withAnnotation(receiver.schema, 'format', 'email'),
|
|
235
233
|
optional: receiver.optional,
|
|
236
234
|
};
|
|
237
|
-
case
|
|
238
|
-
if (kind !==
|
|
239
|
-
throw new Error(
|
|
235
|
+
case 'uuid':
|
|
236
|
+
if (kind !== 'string') {
|
|
237
|
+
throw new Error('uuid() can only be applied to string schemas');
|
|
240
238
|
}
|
|
241
239
|
return {
|
|
242
|
-
schema: withAnnotation(receiver.schema,
|
|
240
|
+
schema: withAnnotation(receiver.schema, 'format', 'uuid'),
|
|
243
241
|
optional: receiver.optional,
|
|
244
242
|
};
|
|
245
|
-
case
|
|
246
|
-
if (kind !==
|
|
247
|
-
throw new Error(
|
|
243
|
+
case 'datetime':
|
|
244
|
+
if (kind !== 'string') {
|
|
245
|
+
throw new Error('datetime() can only be applied to string schemas');
|
|
248
246
|
}
|
|
249
247
|
return {
|
|
250
|
-
schema: withAnnotation(receiver.schema,
|
|
248
|
+
schema: withAnnotation(receiver.schema, 'format', 'date-time'),
|
|
251
249
|
optional: receiver.optional,
|
|
252
250
|
};
|
|
253
|
-
case
|
|
254
|
-
if (kind !==
|
|
255
|
-
throw new Error(
|
|
251
|
+
case 'regex': {
|
|
252
|
+
if (kind !== 'string') {
|
|
253
|
+
throw new Error('regex() can only be applied to string schemas');
|
|
256
254
|
}
|
|
257
255
|
if (args.length === 0)
|
|
258
|
-
throw new Error(
|
|
256
|
+
throw new Error('regex() requires an argument');
|
|
259
257
|
const regex = evaluateLiteralExpression(args[0]);
|
|
260
|
-
if (!isPlainObject(regex) || typeof regex.pattern !==
|
|
261
|
-
throw new Error(
|
|
258
|
+
if (!isPlainObject(regex) || typeof regex.pattern !== 'string') {
|
|
259
|
+
throw new Error('regex() expects a regex literal');
|
|
262
260
|
}
|
|
263
|
-
if (typeof regex.flags ===
|
|
261
|
+
if (typeof regex.flags === 'string' && regex.flags.length > 0) {
|
|
264
262
|
ctx.warnings.push(`Ignored regex flags "${regex.flags}"`);
|
|
265
263
|
}
|
|
266
264
|
return {
|
|
267
|
-
schema: withAnnotation(receiver.schema,
|
|
265
|
+
schema: withAnnotation(receiver.schema, 'pattern', regex.pattern),
|
|
268
266
|
optional: receiver.optional,
|
|
269
267
|
};
|
|
270
268
|
}
|
|
271
|
-
case
|
|
269
|
+
case 'min':
|
|
272
270
|
if (args.length === 0)
|
|
273
|
-
throw new Error(
|
|
271
|
+
throw new Error('min() requires an argument');
|
|
274
272
|
return {
|
|
275
|
-
schema: applyNumericConstraint(receiver.schema, kind,
|
|
273
|
+
schema: applyNumericConstraint(receiver.schema, kind, 'min', Number(evaluateLiteralExpression(args[0]))),
|
|
276
274
|
optional: receiver.optional,
|
|
277
275
|
};
|
|
278
|
-
case
|
|
276
|
+
case 'max':
|
|
279
277
|
if (args.length === 0)
|
|
280
|
-
throw new Error(
|
|
278
|
+
throw new Error('max() requires an argument');
|
|
281
279
|
return {
|
|
282
|
-
schema: applyNumericConstraint(receiver.schema, kind,
|
|
280
|
+
schema: applyNumericConstraint(receiver.schema, kind, 'max', Number(evaluateLiteralExpression(args[0]))),
|
|
283
281
|
optional: receiver.optional,
|
|
284
282
|
};
|
|
285
|
-
case
|
|
283
|
+
case 'length':
|
|
286
284
|
if (args.length === 0)
|
|
287
|
-
throw new Error(
|
|
285
|
+
throw new Error('length() requires an argument');
|
|
288
286
|
return {
|
|
289
|
-
schema: applyNumericConstraint(receiver.schema, kind,
|
|
287
|
+
schema: applyNumericConstraint(receiver.schema, kind, 'length', Number(evaluateLiteralExpression(args[0]))),
|
|
290
288
|
optional: receiver.optional,
|
|
291
289
|
};
|
|
292
|
-
case
|
|
293
|
-
if (kind !==
|
|
294
|
-
throw new Error(
|
|
290
|
+
case 'int': {
|
|
291
|
+
if (kind !== 'number' && kind !== 'integer') {
|
|
292
|
+
throw new Error('int() can only be applied to number schemas');
|
|
295
293
|
}
|
|
296
|
-
const next = withAnnotation(receiver.schema,
|
|
294
|
+
const next = withAnnotation(receiver.schema, 'type', 'integer');
|
|
297
295
|
return { schema: next, optional: receiver.optional };
|
|
298
296
|
}
|
|
299
|
-
case
|
|
300
|
-
if (kind !==
|
|
301
|
-
throw new Error(
|
|
297
|
+
case 'positive': {
|
|
298
|
+
if (kind !== 'number' && kind !== 'integer') {
|
|
299
|
+
throw new Error('positive() can only be applied to number schemas');
|
|
302
300
|
}
|
|
303
|
-
const next = withAnnotation(receiver.schema,
|
|
301
|
+
const next = withAnnotation(receiver.schema, 'exclusiveMinimum', 0);
|
|
304
302
|
return { schema: next, optional: receiver.optional };
|
|
305
303
|
}
|
|
306
|
-
case
|
|
304
|
+
case 'strict': {
|
|
307
305
|
const objectSchema = ensureObjectSchema(receiver.schema);
|
|
308
306
|
return {
|
|
309
307
|
schema: { ...objectSchema, additionalProperties: false },
|
|
310
308
|
optional: receiver.optional,
|
|
311
309
|
};
|
|
312
310
|
}
|
|
313
|
-
case
|
|
311
|
+
case 'partial': {
|
|
314
312
|
// .partial() makes all declared properties optional. For records
|
|
315
313
|
// (open-shape objects) there's nothing to do.
|
|
316
314
|
if (!isPlainObject(receiver.schema)) {
|
|
@@ -319,17 +317,17 @@ function applyMethod(receiver, method, args, ctx) {
|
|
|
319
317
|
const { required: _required, ...rest } = receiver.schema;
|
|
320
318
|
return { schema: rest, optional: receiver.optional };
|
|
321
319
|
}
|
|
322
|
-
case
|
|
320
|
+
case 'array':
|
|
323
321
|
return {
|
|
324
322
|
schema: {
|
|
325
|
-
type:
|
|
323
|
+
type: 'array',
|
|
326
324
|
items: receiver.schema,
|
|
327
325
|
},
|
|
328
326
|
optional: receiver.optional,
|
|
329
327
|
};
|
|
330
|
-
case
|
|
328
|
+
case 'or': {
|
|
331
329
|
if (args.length === 0)
|
|
332
|
-
throw new Error(
|
|
330
|
+
throw new Error('or() requires an argument');
|
|
333
331
|
const other = convertExpression(args[0], ctx);
|
|
334
332
|
return {
|
|
335
333
|
schema: { anyOf: [receiver.schema, other.schema] },
|
|
@@ -345,7 +343,7 @@ function convertObjectShape(objectLiteral, ctx) {
|
|
|
345
343
|
const required = [];
|
|
346
344
|
for (const property of objectLiteral.properties) {
|
|
347
345
|
if (!ts.isPropertyAssignment(property)) {
|
|
348
|
-
throw new Error(
|
|
346
|
+
throw new Error('Only property assignments are supported in z.object()');
|
|
349
347
|
}
|
|
350
348
|
const key = getPropertyName(property.name);
|
|
351
349
|
const child = convertExpression(property.initializer, ctx);
|
|
@@ -355,20 +353,20 @@ function convertObjectShape(objectLiteral, ctx) {
|
|
|
355
353
|
}
|
|
356
354
|
}
|
|
357
355
|
return {
|
|
358
|
-
type:
|
|
356
|
+
type: 'object',
|
|
359
357
|
properties,
|
|
360
358
|
required,
|
|
361
359
|
};
|
|
362
360
|
}
|
|
363
361
|
function convertBaseZodCall(method, args, ctx) {
|
|
364
362
|
switch (method) {
|
|
365
|
-
case
|
|
366
|
-
return { schema: { type:
|
|
367
|
-
case
|
|
368
|
-
return { schema: { type:
|
|
369
|
-
case
|
|
370
|
-
return { schema: { type:
|
|
371
|
-
case
|
|
363
|
+
case 'string':
|
|
364
|
+
return { schema: { type: 'string' }, optional: false };
|
|
365
|
+
case 'number':
|
|
366
|
+
return { schema: { type: 'number' }, optional: false };
|
|
367
|
+
case 'boolean':
|
|
368
|
+
return { schema: { type: 'boolean' }, optional: false };
|
|
369
|
+
case 'enum': {
|
|
372
370
|
// Canonical form: z.enum(["a", "b"]). Also tolerate the hand-written
|
|
373
371
|
// typo z.enum("a", "b") by treating variadic args as an implicit array.
|
|
374
372
|
let elements;
|
|
@@ -379,46 +377,46 @@ function convertBaseZodCall(method, args, ctx) {
|
|
|
379
377
|
elements = args;
|
|
380
378
|
}
|
|
381
379
|
else {
|
|
382
|
-
throw new Error(
|
|
380
|
+
throw new Error('enum() expects at least one argument');
|
|
383
381
|
}
|
|
384
382
|
const values = elements.map((element) => evaluateLiteralExpression(element));
|
|
385
383
|
return { schema: { enum: values }, optional: false };
|
|
386
384
|
}
|
|
387
|
-
case
|
|
385
|
+
case 'literal': {
|
|
388
386
|
if (args.length !== 1)
|
|
389
|
-
throw new Error(
|
|
387
|
+
throw new Error('literal() expects a single argument');
|
|
390
388
|
return { schema: { const: evaluateLiteralExpression(args[0]) }, optional: false };
|
|
391
389
|
}
|
|
392
|
-
case
|
|
390
|
+
case 'never':
|
|
393
391
|
return { schema: false, optional: false };
|
|
394
|
-
case
|
|
392
|
+
case 'array': {
|
|
395
393
|
if (args.length !== 1) {
|
|
396
|
-
throw new Error(
|
|
394
|
+
throw new Error('array() expects a single argument');
|
|
397
395
|
}
|
|
398
396
|
const item = convertExpression(args[0], ctx);
|
|
399
397
|
return {
|
|
400
398
|
schema: {
|
|
401
|
-
type:
|
|
399
|
+
type: 'array',
|
|
402
400
|
items: item.schema,
|
|
403
401
|
},
|
|
404
402
|
optional: false,
|
|
405
403
|
};
|
|
406
404
|
}
|
|
407
|
-
case
|
|
405
|
+
case 'object': {
|
|
408
406
|
if (args.length === 0) {
|
|
409
|
-
return { schema: { type:
|
|
407
|
+
return { schema: { type: 'object' }, optional: false };
|
|
410
408
|
}
|
|
411
409
|
if (args.length !== 1 || !ts.isObjectLiteralExpression(args[0])) {
|
|
412
|
-
throw new Error(
|
|
410
|
+
throw new Error('object() expects a single object literal argument');
|
|
413
411
|
}
|
|
414
412
|
return {
|
|
415
413
|
schema: convertObjectShape(args[0], ctx),
|
|
416
414
|
optional: false,
|
|
417
415
|
};
|
|
418
416
|
}
|
|
419
|
-
case
|
|
417
|
+
case 'record': {
|
|
420
418
|
if (args.length === 0 || args.length > 2) {
|
|
421
|
-
throw new Error(
|
|
419
|
+
throw new Error('record() expects one or two arguments');
|
|
422
420
|
}
|
|
423
421
|
let keySchema;
|
|
424
422
|
let valueIndex = 0;
|
|
@@ -428,7 +426,7 @@ function convertBaseZodCall(method, args, ctx) {
|
|
|
428
426
|
}
|
|
429
427
|
const valueSchema = convertExpression(args[valueIndex], ctx);
|
|
430
428
|
const schema = {
|
|
431
|
-
type:
|
|
429
|
+
type: 'object',
|
|
432
430
|
additionalProperties: valueSchema.schema,
|
|
433
431
|
};
|
|
434
432
|
if (keySchema && isPlainObject(keySchema.schema) && Array.isArray(keySchema.schema.enum)) {
|
|
@@ -436,19 +434,19 @@ function convertBaseZodCall(method, args, ctx) {
|
|
|
436
434
|
}
|
|
437
435
|
return { schema, optional: false };
|
|
438
436
|
}
|
|
439
|
-
case
|
|
437
|
+
case 'union': {
|
|
440
438
|
if (args.length !== 1 || !ts.isArrayLiteralExpression(args[0])) {
|
|
441
|
-
throw new Error(
|
|
439
|
+
throw new Error('union() expects a single array literal argument');
|
|
442
440
|
}
|
|
443
441
|
const options = args[0].elements.map((element) => convertExpression(element, ctx).schema);
|
|
444
442
|
return { schema: { anyOf: options }, optional: false };
|
|
445
443
|
}
|
|
446
|
-
case
|
|
444
|
+
case 'tuple': {
|
|
447
445
|
if (args.length !== 1 || !ts.isArrayLiteralExpression(args[0])) {
|
|
448
|
-
throw new Error(
|
|
446
|
+
throw new Error('tuple() expects a single array literal argument');
|
|
449
447
|
}
|
|
450
448
|
const items = args[0].elements.map((element) => convertExpression(element, ctx).schema);
|
|
451
|
-
const schema = { type:
|
|
449
|
+
const schema = { type: 'array', minItems: items.length, maxItems: items.length };
|
|
452
450
|
if (items.length > 0) {
|
|
453
451
|
schema.prefixItems = items;
|
|
454
452
|
schema.items = false;
|
|
@@ -469,7 +467,7 @@ function convertCallExpression(call, ctx) {
|
|
|
469
467
|
const receiverExpr = unwrapExpression(callee.expression);
|
|
470
468
|
if (ts.isIdentifier(receiverExpr)) {
|
|
471
469
|
const base = receiverExpr.text;
|
|
472
|
-
if (base ===
|
|
470
|
+
if (base === 'z') {
|
|
473
471
|
return convertBaseZodCall(method, call.arguments, ctx);
|
|
474
472
|
}
|
|
475
473
|
}
|
|
@@ -477,8 +475,8 @@ function convertCallExpression(call, ctx) {
|
|
|
477
475
|
return applyMethod(receiver, method, call.arguments, ctx);
|
|
478
476
|
}
|
|
479
477
|
if (ts.isIdentifier(callee)) {
|
|
480
|
-
if (callee.text ===
|
|
481
|
-
throw new Error(
|
|
478
|
+
if (callee.text === 'z') {
|
|
479
|
+
throw new Error('Unexpected bare z() call');
|
|
482
480
|
}
|
|
483
481
|
}
|
|
484
482
|
throw new Error(`Unsupported call expression: ${call.getText()}`);
|
|
@@ -493,22 +491,22 @@ function convertExpression(node, ctx) {
|
|
|
493
491
|
// schema, so the intent is always the method call).
|
|
494
492
|
if (ts.isPropertyAccessExpression(expr)) {
|
|
495
493
|
const method = expr.name.text;
|
|
496
|
-
if (method ===
|
|
494
|
+
if (method === 'nullable' || method === 'optional') {
|
|
497
495
|
const receiver = convertExpression(expr.expression, ctx);
|
|
498
496
|
return applyMethod(receiver, method, ts.factory.createNodeArray([]), ctx);
|
|
499
497
|
}
|
|
500
498
|
}
|
|
501
|
-
if (ts.isIdentifier(expr) && expr.text ===
|
|
499
|
+
if (ts.isIdentifier(expr) && expr.text === 'undefined') {
|
|
502
500
|
return { schema: false, optional: true };
|
|
503
501
|
}
|
|
504
502
|
throw new Error(`Unsupported Zod expression: ${expr.getText()}`);
|
|
505
503
|
}
|
|
506
504
|
export function zodToJsonSchema(source) {
|
|
507
|
-
const sourceFile = ts.createSourceFile(
|
|
505
|
+
const sourceFile = ts.createSourceFile('schema.ts', source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
508
506
|
const ctx = { warnings: [] };
|
|
509
507
|
const expressionStatement = sourceFile.statements.find(ts.isExpressionStatement);
|
|
510
508
|
if (!expressionStatement) {
|
|
511
|
-
throw new Error(
|
|
509
|
+
throw new Error('Expected a Zod expression');
|
|
512
510
|
}
|
|
513
511
|
const result = convertExpression(expressionStatement.expression, ctx);
|
|
514
512
|
if (result.schema !== false && isPlainObject(result.schema)) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { fetchAllChangeHistory, fetchEnvironments } from './launch/api.js';
|
|
2
|
-
import { InvalidSourceConfigError, getOutputPath, isLegacyLogLevel, slugify, transformConfig } from './launch/translate.js';
|
|
2
|
+
import { InvalidSourceConfigError, getOutputPath, isLegacyLogLevel, slugify, transformConfig, } from './launch/translate.js';
|
|
3
3
|
const SOURCE_NAME = 'launch';
|
|
4
4
|
const state = { apiKey: null, droppedOverrides: new Map(), envIdMap: null, skippedConfigs: [] };
|
|
5
5
|
class MissingAuthError extends Error {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const openBrowser: (url: string) => void;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
export const openBrowser = (url) => {
|
|
3
|
+
let command;
|
|
4
|
+
let args;
|
|
5
|
+
if (process.platform === 'darwin') {
|
|
6
|
+
command = 'open';
|
|
7
|
+
args = [url];
|
|
8
|
+
}
|
|
9
|
+
else if (process.platform === 'win32') {
|
|
10
|
+
command = 'cmd';
|
|
11
|
+
args = ['/c', 'start', '""', url.replace(/&/g, '^&')];
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
command = 'xdg-open';
|
|
15
|
+
args = [url];
|
|
16
|
+
}
|
|
17
|
+
try {
|
|
18
|
+
const child = spawn(command, args, { detached: true, stdio: 'ignore' });
|
|
19
|
+
child.on('error', () => {
|
|
20
|
+
// Swallow — user can still copy the URL manually.
|
|
21
|
+
});
|
|
22
|
+
child.unref();
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
// Swallow — user can still copy the URL manually.
|
|
26
|
+
}
|
|
27
|
+
};
|
|
@@ -46,7 +46,9 @@ export async function resolveWorkspaceUuid(command, flagOverride) {
|
|
|
46
46
|
const match = entries.find((w) => w.workspaceSlug === override);
|
|
47
47
|
if (!match) {
|
|
48
48
|
const available = entries.map((w) => w.workspaceSlug).join(', ') || '(none)';
|
|
49
|
-
command.error(`No workspace with slug "${override}" is accessible with this API key. Available: ${available}.`, {
|
|
49
|
+
command.error(`No workspace with slug "${override}" is accessible with this API key. Available: ${available}.`, {
|
|
50
|
+
exit: 1,
|
|
51
|
+
});
|
|
50
52
|
}
|
|
51
53
|
command.verboseLog('ApiKey auth', { source: 'slug', slug: override, workspaceId: match.workspaceId });
|
|
52
54
|
return match.workspaceId;
|
|
@@ -111,7 +113,9 @@ async function recoverAuthConfigFromTokens(command) {
|
|
|
111
113
|
command.error(`Tokens found but failed to fetch workspaces: ${error instanceof Error ? error.message : String(error)}. Run \`qfg login\` to repopulate.`, { exit: 401 });
|
|
112
114
|
}
|
|
113
115
|
if (!res.ok) {
|
|
114
|
-
command.error(`Tokens found but workspace list returned HTTP ${res.status}. Run \`qfg login\` to repopulate.`, {
|
|
116
|
+
command.error(`Tokens found but workspace list returned HTTP ${res.status}. Run \`qfg login\` to repopulate.`, {
|
|
117
|
+
exit: 401,
|
|
118
|
+
});
|
|
115
119
|
}
|
|
116
120
|
const body = (await res.json());
|
|
117
121
|
const entries = (body.json ?? body);
|
package/oclif.manifest.json
CHANGED
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.24",
|
|
5
5
|
"bugs": {
|
|
6
6
|
"url": "https://github.com/quonfig/cli/issues"
|
|
7
7
|
},
|