@shipeasy/openapi 1.2.0 → 1.5.0

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/openapi.json CHANGED
@@ -3362,12 +3362,14 @@
3362
3362
  "semver_gt",
3363
3363
  "semver_gte",
3364
3364
  "semver_lt",
3365
- "semver_lte"
3365
+ "semver_lte",
3366
+ "gate_pass",
3367
+ "exp_in"
3366
3368
  ],
3367
- "description": "Comparison operator. Equality: `eq`/`neq`. Set membership: `in`/`not_in` (value is an array). Numeric order: `gt`/`gte`/`lt`/`lte`. Semver order: `semver_gt`/`semver_gte`/`semver_lt`/`semver_lte` (value is a dotted version like `4.2.0`). String: `contains` (substring), `regex` (JS-flavour pattern, value is the pattern string)."
3369
+ "description": "Comparison operator. Equality: `eq`/`neq`. Set membership: `in`/`not_in` (value is an array). Numeric order: `gt`/`gte`/`lt`/`lte`. Semver order: `semver_gt`/`semver_gte`/`semver_lt`/`semver_lte` (value is a dotted version like `4.2.0`). String: `contains` (substring), `regex` (JS-flavour pattern, value is the pattern string). Cross-resource references (evaluated only on the worker hot path, where both KV blobs load): `gate_pass` (`value` is another gate's name — true when that gate passes for the same caller); `exp_in` (`attr` is a running experiment's name, `value` is a group name, `\"$holdout\"`, or `\"$any\"` — true when the caller is in that variant / the universe holdout / any variant)."
3368
3370
  },
3369
3371
  "value": {
3370
- "description": "Operand. Shape depends on `op`: scalar for `eq/neq/gt/gte/lt/lte/contains/semver_*`; array of scalars for `in/not_in`; string pattern for `regex`."
3372
+ "description": "Operand. Shape depends on `op`: scalar for `eq/neq/gt/gte/lt/lte/contains/semver_*`; array of scalars for `in/not_in`; string pattern for `regex`; referenced gate name for `gate_pass`; group name / `\"$holdout\"` / `\"$any\"` for `exp_in`."
3371
3373
  }
3372
3374
  },
3373
3375
  "required": [
@@ -3447,7 +3449,7 @@
3447
3449
  "id": {
3448
3450
  "type": "string",
3449
3451
  "minLength": 1,
3450
- "description": "Client-supplied stable id for the stack entry (used for React keys, audit diffs)."
3452
+ "description": "Client-supplied stable id for the stack entry (used for React keys, audit diffs, and as the default per-condition rollout salt)."
3451
3453
  },
3452
3454
  "type": {
3453
3455
  "type": "string",
@@ -3505,12 +3507,14 @@
3505
3507
  "semver_gt",
3506
3508
  "semver_gte",
3507
3509
  "semver_lt",
3508
- "semver_lte"
3510
+ "semver_lte",
3511
+ "gate_pass",
3512
+ "exp_in"
3509
3513
  ],
3510
- "description": "Comparison operator. Equality: `eq`/`neq`. Set membership: `in`/`not_in` (value is an array). Numeric order: `gt`/`gte`/`lt`/`lte`. Semver order: `semver_gt`/`semver_gte`/`semver_lt`/`semver_lte` (value is a dotted version like `4.2.0`). String: `contains` (substring), `regex` (JS-flavour pattern, value is the pattern string)."
3514
+ "description": "Comparison operator. Equality: `eq`/`neq`. Set membership: `in`/`not_in` (value is an array). Numeric order: `gt`/`gte`/`lt`/`lte`. Semver order: `semver_gt`/`semver_gte`/`semver_lt`/`semver_lte` (value is a dotted version like `4.2.0`). String: `contains` (substring), `regex` (JS-flavour pattern, value is the pattern string). Cross-resource references (evaluated only on the worker hot path, where both KV blobs load): `gate_pass` (`value` is another gate's name — true when that gate passes for the same caller); `exp_in` (`attr` is a running experiment's name, `value` is a group name, `\"$holdout\"`, or `\"$any\"` — true when the caller is in that variant / the universe holdout / any variant)."
3511
3515
  },
3512
3516
  "value": {
3513
- "description": "Operand. Shape depends on `op`: scalar for `eq/neq/gt/gte/lt/lte/contains/semver_*`; array of scalars for `in/not_in`; string pattern for `regex`."
3517
+ "description": "Operand. Shape depends on `op`: scalar for `eq/neq/gt/gte/lt/lte/contains/semver_*`; array of scalars for `in/not_in`; string pattern for `regex`; referenced gate name for `gate_pass`; group name / `\"$holdout\"` / `\"$any\"` for `exp_in`."
3514
3518
  }
3515
3519
  },
3516
3520
  "required": [
@@ -3522,6 +3526,57 @@
3522
3526
  "description": "Single targeting predicate. A gate's `rules` array is evaluated with AND semantics; use the gatekeeper `stack` for OR / fall-through logic."
3523
3527
  }
3524
3528
  },
3529
+ "rolloutPct": {
3530
+ "description": "Per-condition rollout in basis points. After the rules match, the caller is bucketed at this %; in-bucket → gate `true`, otherwise fall through to the next entry. Absent ⇒ 10000 (100%, every matching caller passes).",
3531
+ "type": "integer",
3532
+ "minimum": 0,
3533
+ "maximum": 10000
3534
+ },
3535
+ "bucketBy": {
3536
+ "description": "Attribute used to hash the caller into the per-condition bucket. Default `userID`.",
3537
+ "type": "string"
3538
+ },
3539
+ "salt": {
3540
+ "description": "Per-condition hash salt. Defaults to the entry `id`, then the gate salt.",
3541
+ "type": "string"
3542
+ },
3543
+ "ramp": {
3544
+ "type": "object",
3545
+ "properties": {
3546
+ "from": {
3547
+ "type": "integer",
3548
+ "minimum": 0,
3549
+ "maximum": 10000,
3550
+ "description": "Start of the ramp in basis points (0–10000)."
3551
+ },
3552
+ "to": {
3553
+ "type": "integer",
3554
+ "minimum": 0,
3555
+ "maximum": 10000,
3556
+ "description": "End of the ramp in basis points (0–10000)."
3557
+ },
3558
+ "startAt": {
3559
+ "type": "integer",
3560
+ "minimum": 0,
3561
+ "maximum": 9007199254740991,
3562
+ "description": "Ramp start, epoch milliseconds (UTC)."
3563
+ },
3564
+ "durationMs": {
3565
+ "type": "integer",
3566
+ "exclusiveMinimum": 0,
3567
+ "maximum": 9007199254740991,
3568
+ "description": "Ramp length in milliseconds."
3569
+ }
3570
+ },
3571
+ "required": [
3572
+ "from",
3573
+ "to",
3574
+ "startAt",
3575
+ "durationMs"
3576
+ ],
3577
+ "additionalProperties": false,
3578
+ "description": "Linear rollout ramp. The effective rollout % interpolates `from`→`to` over `[startAt, startAt+durationMs]`, clamped outside that window, evaluated against the wall-clock at eval time. Overrides the entry's static `rolloutPct` while present."
3579
+ },
3525
3580
  "locked": {
3526
3581
  "description": "If `true`, dashboard hides edit controls (e.g. the trailing public-floor entry).",
3527
3582
  "type": "boolean"
@@ -3533,7 +3588,7 @@
3533
3588
  "rules"
3534
3589
  ],
3535
3590
  "additionalProperties": false,
3536
- "description": "Rule-based sub-gate. When matched, evaluation short-circuits and the gate returns `true` for the current caller."
3591
+ "description": "Rule-based sub-gate. When the rules match and the caller falls in the per-condition rollout bucket, evaluation short-circuits and the gate returns `true`; otherwise it falls through to the next entry."
3537
3592
  },
3538
3593
  {
3539
3594
  "type": "object",
@@ -3577,6 +3632,43 @@
3577
3632
  "description": "Per-entry hash salt. Defaults to the gate's salt. Set explicitly to keep two rollouts independent or correlated across gates.",
3578
3633
  "type": "string"
3579
3634
  },
3635
+ "ramp": {
3636
+ "type": "object",
3637
+ "properties": {
3638
+ "from": {
3639
+ "type": "integer",
3640
+ "minimum": 0,
3641
+ "maximum": 10000,
3642
+ "description": "Start of the ramp in basis points (0–10000)."
3643
+ },
3644
+ "to": {
3645
+ "type": "integer",
3646
+ "minimum": 0,
3647
+ "maximum": 10000,
3648
+ "description": "End of the ramp in basis points (0–10000)."
3649
+ },
3650
+ "startAt": {
3651
+ "type": "integer",
3652
+ "minimum": 0,
3653
+ "maximum": 9007199254740991,
3654
+ "description": "Ramp start, epoch milliseconds (UTC)."
3655
+ },
3656
+ "durationMs": {
3657
+ "type": "integer",
3658
+ "exclusiveMinimum": 0,
3659
+ "maximum": 9007199254740991,
3660
+ "description": "Ramp length in milliseconds."
3661
+ }
3662
+ },
3663
+ "required": [
3664
+ "from",
3665
+ "to",
3666
+ "startAt",
3667
+ "durationMs"
3668
+ ],
3669
+ "additionalProperties": false,
3670
+ "description": "Linear rollout ramp. The effective rollout % interpolates `from`→`to` over `[startAt, startAt+durationMs]`, clamped outside that window, evaluated against the wall-clock at eval time. Overrides the entry's static `rolloutPct` while present."
3671
+ },
3580
3672
  "locked": {
3581
3673
  "description": "If `true`, dashboard hides edit controls.",
3582
3674
  "type": "boolean"
@@ -3588,7 +3680,7 @@
3588
3680
  "rolloutPct"
3589
3681
  ],
3590
3682
  "additionalProperties": false,
3591
- "description": "Percentage-rollout sub-gate. Hashes `bucketBy` (default `userID`) with `salt` and returns `true` for `rolloutPct/10000` fraction of callers."
3683
+ "description": "Percentage-rollout sub-gate. Hashes `bucketBy` (default `userID`) with `salt` and returns `true` for `rolloutPct/10000` fraction of callers (or the ramp's effective % when `ramp` is set)."
3592
3684
  }
3593
3685
  ],
3594
3686
  "description": "One entry in the ordered gatekeeper stack. Evaluated top-to-bottom; first matching entry decides. Mix `condition` and `rollout` entries to express `internal-only` + `1% beta` + `50% public` in a single gate."
@@ -3686,12 +3778,14 @@
3686
3778
  "semver_gt",
3687
3779
  "semver_gte",
3688
3780
  "semver_lt",
3689
- "semver_lte"
3781
+ "semver_lte",
3782
+ "gate_pass",
3783
+ "exp_in"
3690
3784
  ],
3691
- "description": "Comparison operator. Equality: `eq`/`neq`. Set membership: `in`/`not_in` (value is an array). Numeric order: `gt`/`gte`/`lt`/`lte`. Semver order: `semver_gt`/`semver_gte`/`semver_lt`/`semver_lte` (value is a dotted version like `4.2.0`). String: `contains` (substring), `regex` (JS-flavour pattern, value is the pattern string)."
3785
+ "description": "Comparison operator. Equality: `eq`/`neq`. Set membership: `in`/`not_in` (value is an array). Numeric order: `gt`/`gte`/`lt`/`lte`. Semver order: `semver_gt`/`semver_gte`/`semver_lt`/`semver_lte` (value is a dotted version like `4.2.0`). String: `contains` (substring), `regex` (JS-flavour pattern, value is the pattern string). Cross-resource references (evaluated only on the worker hot path, where both KV blobs load): `gate_pass` (`value` is another gate's name — true when that gate passes for the same caller); `exp_in` (`attr` is a running experiment's name, `value` is a group name, `\"$holdout\"`, or `\"$any\"` — true when the caller is in that variant / the universe holdout / any variant)."
3692
3786
  },
3693
3787
  "value": {
3694
- "description": "Operand. Shape depends on `op`: scalar for `eq/neq/gt/gte/lt/lte/contains/semver_*`; array of scalars for `in/not_in`; string pattern for `regex`."
3788
+ "description": "Operand. Shape depends on `op`: scalar for `eq/neq/gt/gte/lt/lte/contains/semver_*`; array of scalars for `in/not_in`; string pattern for `regex`; referenced gate name for `gate_pass`; group name / `\"$holdout\"` / `\"$any\"` for `exp_in`."
3695
3789
  }
3696
3790
  },
3697
3791
  "required": [
@@ -3722,7 +3816,7 @@
3722
3816
  "id": {
3723
3817
  "type": "string",
3724
3818
  "minLength": 1,
3725
- "description": "Client-supplied stable id for the stack entry (used for React keys, audit diffs)."
3819
+ "description": "Client-supplied stable id for the stack entry (used for React keys, audit diffs, and as the default per-condition rollout salt)."
3726
3820
  },
3727
3821
  "type": {
3728
3822
  "type": "string",
@@ -3780,12 +3874,14 @@
3780
3874
  "semver_gt",
3781
3875
  "semver_gte",
3782
3876
  "semver_lt",
3783
- "semver_lte"
3877
+ "semver_lte",
3878
+ "gate_pass",
3879
+ "exp_in"
3784
3880
  ],
3785
- "description": "Comparison operator. Equality: `eq`/`neq`. Set membership: `in`/`not_in` (value is an array). Numeric order: `gt`/`gte`/`lt`/`lte`. Semver order: `semver_gt`/`semver_gte`/`semver_lt`/`semver_lte` (value is a dotted version like `4.2.0`). String: `contains` (substring), `regex` (JS-flavour pattern, value is the pattern string)."
3881
+ "description": "Comparison operator. Equality: `eq`/`neq`. Set membership: `in`/`not_in` (value is an array). Numeric order: `gt`/`gte`/`lt`/`lte`. Semver order: `semver_gt`/`semver_gte`/`semver_lt`/`semver_lte` (value is a dotted version like `4.2.0`). String: `contains` (substring), `regex` (JS-flavour pattern, value is the pattern string). Cross-resource references (evaluated only on the worker hot path, where both KV blobs load): `gate_pass` (`value` is another gate's name — true when that gate passes for the same caller); `exp_in` (`attr` is a running experiment's name, `value` is a group name, `\"$holdout\"`, or `\"$any\"` — true when the caller is in that variant / the universe holdout / any variant)."
3786
3882
  },
3787
3883
  "value": {
3788
- "description": "Operand. Shape depends on `op`: scalar for `eq/neq/gt/gte/lt/lte/contains/semver_*`; array of scalars for `in/not_in`; string pattern for `regex`."
3884
+ "description": "Operand. Shape depends on `op`: scalar for `eq/neq/gt/gte/lt/lte/contains/semver_*`; array of scalars for `in/not_in`; string pattern for `regex`; referenced gate name for `gate_pass`; group name / `\"$holdout\"` / `\"$any\"` for `exp_in`."
3789
3885
  }
3790
3886
  },
3791
3887
  "required": [
@@ -3797,6 +3893,57 @@
3797
3893
  "description": "Single targeting predicate. A gate's `rules` array is evaluated with AND semantics; use the gatekeeper `stack` for OR / fall-through logic."
3798
3894
  }
3799
3895
  },
3896
+ "rolloutPct": {
3897
+ "description": "Per-condition rollout in basis points. After the rules match, the caller is bucketed at this %; in-bucket → gate `true`, otherwise fall through to the next entry. Absent ⇒ 10000 (100%, every matching caller passes).",
3898
+ "type": "integer",
3899
+ "minimum": 0,
3900
+ "maximum": 10000
3901
+ },
3902
+ "bucketBy": {
3903
+ "description": "Attribute used to hash the caller into the per-condition bucket. Default `userID`.",
3904
+ "type": "string"
3905
+ },
3906
+ "salt": {
3907
+ "description": "Per-condition hash salt. Defaults to the entry `id`, then the gate salt.",
3908
+ "type": "string"
3909
+ },
3910
+ "ramp": {
3911
+ "type": "object",
3912
+ "properties": {
3913
+ "from": {
3914
+ "type": "integer",
3915
+ "minimum": 0,
3916
+ "maximum": 10000,
3917
+ "description": "Start of the ramp in basis points (0–10000)."
3918
+ },
3919
+ "to": {
3920
+ "type": "integer",
3921
+ "minimum": 0,
3922
+ "maximum": 10000,
3923
+ "description": "End of the ramp in basis points (0–10000)."
3924
+ },
3925
+ "startAt": {
3926
+ "type": "integer",
3927
+ "minimum": 0,
3928
+ "maximum": 9007199254740991,
3929
+ "description": "Ramp start, epoch milliseconds (UTC)."
3930
+ },
3931
+ "durationMs": {
3932
+ "type": "integer",
3933
+ "exclusiveMinimum": 0,
3934
+ "maximum": 9007199254740991,
3935
+ "description": "Ramp length in milliseconds."
3936
+ }
3937
+ },
3938
+ "required": [
3939
+ "from",
3940
+ "to",
3941
+ "startAt",
3942
+ "durationMs"
3943
+ ],
3944
+ "additionalProperties": false,
3945
+ "description": "Linear rollout ramp. The effective rollout % interpolates `from`→`to` over `[startAt, startAt+durationMs]`, clamped outside that window, evaluated against the wall-clock at eval time. Overrides the entry's static `rolloutPct` while present."
3946
+ },
3800
3947
  "locked": {
3801
3948
  "description": "If `true`, dashboard hides edit controls (e.g. the trailing public-floor entry).",
3802
3949
  "type": "boolean"
@@ -3808,7 +3955,7 @@
3808
3955
  "rules"
3809
3956
  ],
3810
3957
  "additionalProperties": false,
3811
- "description": "Rule-based sub-gate. When matched, evaluation short-circuits and the gate returns `true` for the current caller."
3958
+ "description": "Rule-based sub-gate. When the rules match and the caller falls in the per-condition rollout bucket, evaluation short-circuits and the gate returns `true`; otherwise it falls through to the next entry."
3812
3959
  },
3813
3960
  {
3814
3961
  "type": "object",
@@ -3852,6 +3999,43 @@
3852
3999
  "description": "Per-entry hash salt. Defaults to the gate's salt. Set explicitly to keep two rollouts independent or correlated across gates.",
3853
4000
  "type": "string"
3854
4001
  },
4002
+ "ramp": {
4003
+ "type": "object",
4004
+ "properties": {
4005
+ "from": {
4006
+ "type": "integer",
4007
+ "minimum": 0,
4008
+ "maximum": 10000,
4009
+ "description": "Start of the ramp in basis points (0–10000)."
4010
+ },
4011
+ "to": {
4012
+ "type": "integer",
4013
+ "minimum": 0,
4014
+ "maximum": 10000,
4015
+ "description": "End of the ramp in basis points (0–10000)."
4016
+ },
4017
+ "startAt": {
4018
+ "type": "integer",
4019
+ "minimum": 0,
4020
+ "maximum": 9007199254740991,
4021
+ "description": "Ramp start, epoch milliseconds (UTC)."
4022
+ },
4023
+ "durationMs": {
4024
+ "type": "integer",
4025
+ "exclusiveMinimum": 0,
4026
+ "maximum": 9007199254740991,
4027
+ "description": "Ramp length in milliseconds."
4028
+ }
4029
+ },
4030
+ "required": [
4031
+ "from",
4032
+ "to",
4033
+ "startAt",
4034
+ "durationMs"
4035
+ ],
4036
+ "additionalProperties": false,
4037
+ "description": "Linear rollout ramp. The effective rollout % interpolates `from`→`to` over `[startAt, startAt+durationMs]`, clamped outside that window, evaluated against the wall-clock at eval time. Overrides the entry's static `rolloutPct` while present."
4038
+ },
3855
4039
  "locked": {
3856
4040
  "description": "If `true`, dashboard hides edit controls.",
3857
4041
  "type": "boolean"
@@ -3863,7 +4047,7 @@
3863
4047
  "rolloutPct"
3864
4048
  ],
3865
4049
  "additionalProperties": false,
3866
- "description": "Percentage-rollout sub-gate. Hashes `bucketBy` (default `userID`) with `salt` and returns `true` for `rolloutPct/10000` fraction of callers."
4050
+ "description": "Percentage-rollout sub-gate. Hashes `bucketBy` (default `userID`) with `salt` and returns `true` for `rolloutPct/10000` fraction of callers (or the ramp's effective % when `ramp` is set)."
3867
4051
  }
3868
4052
  ],
3869
4053
  "description": "One entry in the ordered gatekeeper stack. Evaluated top-to-bottom; first matching entry decides. Mix `condition` and `rollout` entries to express `internal-only` + `1% beta` + `50% public` in a single gate."
@@ -3979,12 +4163,14 @@
3979
4163
  "semver_gt",
3980
4164
  "semver_gte",
3981
4165
  "semver_lt",
3982
- "semver_lte"
4166
+ "semver_lte",
4167
+ "gate_pass",
4168
+ "exp_in"
3983
4169
  ],
3984
- "description": "Comparison operator. Equality: `eq`/`neq`. Set membership: `in`/`not_in` (value is an array). Numeric order: `gt`/`gte`/`lt`/`lte`. Semver order: `semver_gt`/`semver_gte`/`semver_lt`/`semver_lte` (value is a dotted version like `4.2.0`). String: `contains` (substring), `regex` (JS-flavour pattern, value is the pattern string)."
4170
+ "description": "Comparison operator. Equality: `eq`/`neq`. Set membership: `in`/`not_in` (value is an array). Numeric order: `gt`/`gte`/`lt`/`lte`. Semver order: `semver_gt`/`semver_gte`/`semver_lt`/`semver_lte` (value is a dotted version like `4.2.0`). String: `contains` (substring), `regex` (JS-flavour pattern, value is the pattern string). Cross-resource references (evaluated only on the worker hot path, where both KV blobs load): `gate_pass` (`value` is another gate's name — true when that gate passes for the same caller); `exp_in` (`attr` is a running experiment's name, `value` is a group name, `\"$holdout\"`, or `\"$any\"` — true when the caller is in that variant / the universe holdout / any variant)."
3985
4171
  },
3986
4172
  "value": {
3987
- "description": "Operand. Shape depends on `op`: scalar for `eq/neq/gt/gte/lt/lte/contains/semver_*`; array of scalars for `in/not_in`; string pattern for `regex`."
4173
+ "description": "Operand. Shape depends on `op`: scalar for `eq/neq/gt/gte/lt/lte/contains/semver_*`; array of scalars for `in/not_in`; string pattern for `regex`; referenced gate name for `gate_pass`; group name / `\"$holdout\"` / `\"$any\"` for `exp_in`."
3988
4174
  }
3989
4175
  },
3990
4176
  "required": [
@@ -4013,7 +4199,7 @@
4013
4199
  "id": {
4014
4200
  "type": "string",
4015
4201
  "minLength": 1,
4016
- "description": "Client-supplied stable id for the stack entry (used for React keys, audit diffs)."
4202
+ "description": "Client-supplied stable id for the stack entry (used for React keys, audit diffs, and as the default per-condition rollout salt)."
4017
4203
  },
4018
4204
  "type": {
4019
4205
  "type": "string",
@@ -4071,12 +4257,14 @@
4071
4257
  "semver_gt",
4072
4258
  "semver_gte",
4073
4259
  "semver_lt",
4074
- "semver_lte"
4260
+ "semver_lte",
4261
+ "gate_pass",
4262
+ "exp_in"
4075
4263
  ],
4076
- "description": "Comparison operator. Equality: `eq`/`neq`. Set membership: `in`/`not_in` (value is an array). Numeric order: `gt`/`gte`/`lt`/`lte`. Semver order: `semver_gt`/`semver_gte`/`semver_lt`/`semver_lte` (value is a dotted version like `4.2.0`). String: `contains` (substring), `regex` (JS-flavour pattern, value is the pattern string)."
4264
+ "description": "Comparison operator. Equality: `eq`/`neq`. Set membership: `in`/`not_in` (value is an array). Numeric order: `gt`/`gte`/`lt`/`lte`. Semver order: `semver_gt`/`semver_gte`/`semver_lt`/`semver_lte` (value is a dotted version like `4.2.0`). String: `contains` (substring), `regex` (JS-flavour pattern, value is the pattern string). Cross-resource references (evaluated only on the worker hot path, where both KV blobs load): `gate_pass` (`value` is another gate's name — true when that gate passes for the same caller); `exp_in` (`attr` is a running experiment's name, `value` is a group name, `\"$holdout\"`, or `\"$any\"` — true when the caller is in that variant / the universe holdout / any variant)."
4077
4265
  },
4078
4266
  "value": {
4079
- "description": "Operand. Shape depends on `op`: scalar for `eq/neq/gt/gte/lt/lte/contains/semver_*`; array of scalars for `in/not_in`; string pattern for `regex`."
4267
+ "description": "Operand. Shape depends on `op`: scalar for `eq/neq/gt/gte/lt/lte/contains/semver_*`; array of scalars for `in/not_in`; string pattern for `regex`; referenced gate name for `gate_pass`; group name / `\"$holdout\"` / `\"$any\"` for `exp_in`."
4080
4268
  }
4081
4269
  },
4082
4270
  "required": [
@@ -4088,6 +4276,57 @@
4088
4276
  "description": "Single targeting predicate. A gate's `rules` array is evaluated with AND semantics; use the gatekeeper `stack` for OR / fall-through logic."
4089
4277
  }
4090
4278
  },
4279
+ "rolloutPct": {
4280
+ "description": "Per-condition rollout in basis points. After the rules match, the caller is bucketed at this %; in-bucket → gate `true`, otherwise fall through to the next entry. Absent ⇒ 10000 (100%, every matching caller passes).",
4281
+ "type": "integer",
4282
+ "minimum": 0,
4283
+ "maximum": 10000
4284
+ },
4285
+ "bucketBy": {
4286
+ "description": "Attribute used to hash the caller into the per-condition bucket. Default `userID`.",
4287
+ "type": "string"
4288
+ },
4289
+ "salt": {
4290
+ "description": "Per-condition hash salt. Defaults to the entry `id`, then the gate salt.",
4291
+ "type": "string"
4292
+ },
4293
+ "ramp": {
4294
+ "type": "object",
4295
+ "properties": {
4296
+ "from": {
4297
+ "type": "integer",
4298
+ "minimum": 0,
4299
+ "maximum": 10000,
4300
+ "description": "Start of the ramp in basis points (0–10000)."
4301
+ },
4302
+ "to": {
4303
+ "type": "integer",
4304
+ "minimum": 0,
4305
+ "maximum": 10000,
4306
+ "description": "End of the ramp in basis points (0–10000)."
4307
+ },
4308
+ "startAt": {
4309
+ "type": "integer",
4310
+ "minimum": 0,
4311
+ "maximum": 9007199254740991,
4312
+ "description": "Ramp start, epoch milliseconds (UTC)."
4313
+ },
4314
+ "durationMs": {
4315
+ "type": "integer",
4316
+ "exclusiveMinimum": 0,
4317
+ "maximum": 9007199254740991,
4318
+ "description": "Ramp length in milliseconds."
4319
+ }
4320
+ },
4321
+ "required": [
4322
+ "from",
4323
+ "to",
4324
+ "startAt",
4325
+ "durationMs"
4326
+ ],
4327
+ "additionalProperties": false,
4328
+ "description": "Linear rollout ramp. The effective rollout % interpolates `from`→`to` over `[startAt, startAt+durationMs]`, clamped outside that window, evaluated against the wall-clock at eval time. Overrides the entry's static `rolloutPct` while present."
4329
+ },
4091
4330
  "locked": {
4092
4331
  "description": "If `true`, dashboard hides edit controls (e.g. the trailing public-floor entry).",
4093
4332
  "type": "boolean"
@@ -4099,7 +4338,7 @@
4099
4338
  "rules"
4100
4339
  ],
4101
4340
  "additionalProperties": false,
4102
- "description": "Rule-based sub-gate. When matched, evaluation short-circuits and the gate returns `true` for the current caller."
4341
+ "description": "Rule-based sub-gate. When the rules match and the caller falls in the per-condition rollout bucket, evaluation short-circuits and the gate returns `true`; otherwise it falls through to the next entry."
4103
4342
  },
4104
4343
  {
4105
4344
  "type": "object",
@@ -4143,6 +4382,43 @@
4143
4382
  "description": "Per-entry hash salt. Defaults to the gate's salt. Set explicitly to keep two rollouts independent or correlated across gates.",
4144
4383
  "type": "string"
4145
4384
  },
4385
+ "ramp": {
4386
+ "type": "object",
4387
+ "properties": {
4388
+ "from": {
4389
+ "type": "integer",
4390
+ "minimum": 0,
4391
+ "maximum": 10000,
4392
+ "description": "Start of the ramp in basis points (0–10000)."
4393
+ },
4394
+ "to": {
4395
+ "type": "integer",
4396
+ "minimum": 0,
4397
+ "maximum": 10000,
4398
+ "description": "End of the ramp in basis points (0–10000)."
4399
+ },
4400
+ "startAt": {
4401
+ "type": "integer",
4402
+ "minimum": 0,
4403
+ "maximum": 9007199254740991,
4404
+ "description": "Ramp start, epoch milliseconds (UTC)."
4405
+ },
4406
+ "durationMs": {
4407
+ "type": "integer",
4408
+ "exclusiveMinimum": 0,
4409
+ "maximum": 9007199254740991,
4410
+ "description": "Ramp length in milliseconds."
4411
+ }
4412
+ },
4413
+ "required": [
4414
+ "from",
4415
+ "to",
4416
+ "startAt",
4417
+ "durationMs"
4418
+ ],
4419
+ "additionalProperties": false,
4420
+ "description": "Linear rollout ramp. The effective rollout % interpolates `from`→`to` over `[startAt, startAt+durationMs]`, clamped outside that window, evaluated against the wall-clock at eval time. Overrides the entry's static `rolloutPct` while present."
4421
+ },
4146
4422
  "locked": {
4147
4423
  "description": "If `true`, dashboard hides edit controls.",
4148
4424
  "type": "boolean"
@@ -4154,7 +4430,7 @@
4154
4430
  "rolloutPct"
4155
4431
  ],
4156
4432
  "additionalProperties": false,
4157
- "description": "Percentage-rollout sub-gate. Hashes `bucketBy` (default `userID`) with `salt` and returns `true` for `rolloutPct/10000` fraction of callers."
4433
+ "description": "Percentage-rollout sub-gate. Hashes `bucketBy` (default `userID`) with `salt` and returns `true` for `rolloutPct/10000` fraction of callers (or the ramp's effective % when `ramp` is set)."
4158
4434
  }
4159
4435
  ],
4160
4436
  "description": "One entry in the ordered gatekeeper stack. Evaluated top-to-bottom; first matching entry decides. Mix `condition` and `rollout` entries to express `internal-only` + `1% beta` + `50% public` in a single gate."
@@ -4333,12 +4609,10 @@
4333
4609
  "anyOf": [
4334
4610
  {
4335
4611
  "type": "string",
4336
- "enum": [
4337
- "user_id",
4338
- "session_id",
4339
- "device_id"
4340
- ],
4341
- "description": "Identifier used as the bucketing key. `user_id` is sticky across sessions."
4612
+ "minLength": 1,
4613
+ "maxLength": 128,
4614
+ "pattern": "^[a-zA-Z_][a-zA-Z0-9_]*$",
4615
+ "description": "User-attribute name used as the bucketing key — e.g. `user_id` (default), `session_id`, `device_id`, or a custom attribute like `company_id` to keep a whole org on one variant. Defaults to `user_id` when omitted."
4342
4616
  },
4343
4617
  {
4344
4618
  "type": "null"
@@ -4583,12 +4857,10 @@
4583
4857
  "anyOf": [
4584
4858
  {
4585
4859
  "type": "string",
4586
- "enum": [
4587
- "user_id",
4588
- "session_id",
4589
- "device_id"
4590
- ],
4591
- "description": "Identifier used as the bucketing key. `user_id` is sticky across sessions."
4860
+ "minLength": 1,
4861
+ "maxLength": 128,
4862
+ "pattern": "^[a-zA-Z_][a-zA-Z0-9_]*$",
4863
+ "description": "User-attribute name used as the bucketing key — e.g. `user_id` (default), `session_id`, `device_id`, or a custom attribute like `company_id` to keep a whole org on one variant. Defaults to `user_id` when omitted."
4592
4864
  },
4593
4865
  {
4594
4866
  "type": "null"
@@ -4827,12 +5099,10 @@
4827
5099
  "anyOf": [
4828
5100
  {
4829
5101
  "type": "string",
4830
- "enum": [
4831
- "user_id",
4832
- "session_id",
4833
- "device_id"
4834
- ],
4835
- "description": "Identifier used as the bucketing key. `user_id` is sticky across sessions."
5102
+ "minLength": 1,
5103
+ "maxLength": 128,
5104
+ "pattern": "^[a-zA-Z_][a-zA-Z0-9_]*$",
5105
+ "description": "User-attribute name used as the bucketing key — e.g. `user_id` (default), `session_id`, `device_id`, or a custom attribute like `company_id` to keep a whole org on one variant. Defaults to `user_id` when omitted."
4836
5106
  },
4837
5107
  {
4838
5108
  "type": "null"
@@ -5114,12 +5384,10 @@
5114
5384
  "anyOf": [
5115
5385
  {
5116
5386
  "type": "string",
5117
- "enum": [
5118
- "user_id",
5119
- "session_id",
5120
- "device_id"
5121
- ],
5122
- "description": "Identifier used as the bucketing key. `user_id` is sticky across sessions."
5387
+ "minLength": 1,
5388
+ "maxLength": 128,
5389
+ "pattern": "^[a-zA-Z_][a-zA-Z0-9_]*$",
5390
+ "description": "User-attribute name used as the bucketing key — e.g. `user_id` (default), `session_id`, `device_id`, or a custom attribute like `company_id` to keep a whole org on one variant. Defaults to `user_id` when omitted."
5123
5391
  },
5124
5392
  {
5125
5393
  "type": "null"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shipeasy/openapi",
3
- "version": "1.2.0",
3
+ "version": "1.5.0",
4
4
  "description": "Shipeasy admin OpenAPI spec + typed client. Single source of truth for endpoint paths, request/response shapes, and the resource registry consumed by @shipeasy/cli and @shipeasy/mcp.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",