@mozilla/nimbus-schemas 2025.1.1 → 3001.0.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/README.md CHANGED
@@ -10,29 +10,55 @@ This directory contains a package of schemas published to various repositories f
10
10
  - node ^16
11
11
  - yarn ^1.22
12
12
 
13
- #### Common Operations
13
+ ### Common Operations
14
14
  From project root (i.e., parent to this directory)
15
15
  - Build: `make schemas_build`
16
16
  - Run linting and tests: `make schemas_check`
17
17
  - Code formatting: `make schemas_format`
18
18
 
19
- #### Building Python Schemas Package
20
- `make schemas_build_pypi`
21
19
 
22
- #### Building Typescript Schemas Package
23
- `make schemas_build_npm`
20
+ ### Adding New Schemas
21
+
22
+ **Example**
23
+ Creating a new sub-package `new_stuff`
24
+ 1. Add new directory `mozilla_nimbus_schemas` / `new_stuff` with schemas inside.
25
+ 1. Add `new_stuff` schemas to the `new_stuff` directory's `__init__.py` file.
26
+ 1. Add `new_stuff` to top level `__init__.py`:
27
+ >`from mozilla_nimbus_schemas.new_stuff import *`
28
+ 1. Generate Typescript and/or JSON Schemas for `new_stuff` by updating the `generate_json_schema.py` script like so:
29
+
30
+ a. Import `new_stuff` alongside existing `mozilla_nimbus_schemas` imports, e.g.,
31
+ >`from mozilla_nimbus_schemas import experiments, jetstream, new_stuff`
32
+
33
+ b. (optional) Add `new_stuff` to Typescript generation
34
+ * Update `TS_SCHEMA_PACKAGES` list, e.g.,
35
+ >`TS_SCHEMA_PACKAGES = [experiments, jetstream, new_stuff]`
36
+
37
+ c. (optional) Add `new_stuff` to JSON Schema generation
38
+ * Update `JSON_SCHEMA_PACKAGES` list, e.g.,
39
+ >`JSON_SCHEMA_PACKAGES = [experiments, new_stuff]`
40
+
41
+ 1. Build everything with `make schemas_build`
42
+
43
+ 1. Update the version (see [Versioning](#versioningversioning) below).
24
44
 
25
45
  ## Schemas
26
46
  ### Jetstream
27
47
 
28
48
  Contains schemas describing analysis results, metadata, and errors from [Jetstream](https://github.com/mozilla/jetstream).
29
49
 
50
+ ### Experiments
51
+
52
+ Defines the schemas used for validating the Experimenter v6 API. Previously defined in the now-deprecated [`nimbus-shared`](https://github.com/mozilla/nimbus-shared).
53
+
30
54
 
31
55
  ## Deployment
32
56
  The build and deployment occurs automatically through CI. A deployment is triggered on merges into the `main` branch when the version number changes. Schemas are published to various repos for access in different languages.
33
57
 
34
- #### Versioning
35
- `mozilla-nimbus-schemas` uses a date-based versioning scheme (`CalVer`). The format is `yyyy.m.MINOR`, where `m` is the non-zero-padded month, and `MINOR` is an incrementing number starting from 1 for each month. Notably, this `MINOR` number does NOT correspond to the day of the month. For example, the second release in June of 2023 would have a version of `2023.6.2`.
58
+ ### Versioning
59
+ `mozilla-nimbus-schemas` uses semantic versioning (`semver`) of `major.minor.patch`. Previously, we
60
+ used a date-based versioning scheme (`CalVer`), which is why the version is so high (3000+). Any
61
+ breaking changes should result in an increase in the major version.
36
62
 
37
63
  #### Version Updates
38
64
  1. To update the published package versions, update the `VERSION` file in this directory.
package/index.d.ts CHANGED
@@ -7,7 +7,7 @@
7
7
  */
8
8
 
9
9
  /**
10
- * A unique, stable indentifier for the user used as an input to bucket hashing.
10
+ * A unique, stable identifier for the user used as an input to bucket hashing.
11
11
  */
12
12
  export type RandomizationUnit = "normandy_id" | "nimbus_id" | "user_id" | "group_id";
13
13
  export type DesktopApplication = "firefox-desktop" | "firefox-desktop-background-task";
@@ -100,7 +100,7 @@ export interface DesktopAllVersionsNimbusExperiment {
100
100
  /**
101
101
  * A list of featureIds the experiment contains configurations for.
102
102
  */
103
- featureIds?: string[];
103
+ featureIds: string[];
104
104
  /**
105
105
  * A JEXL targeting expression used to filter out experiments.
106
106
  */
@@ -145,6 +145,7 @@ export interface DesktopAllVersionsNimbusExperiment {
145
145
  * If null, all locales are targeted.
146
146
  */
147
147
  locales?: string[] | null;
148
+ localizations?: ExperimentLocalizations | null;
148
149
  /**
149
150
  * The date that this experiment was first published to Remote Settings.
150
151
  *
@@ -187,7 +188,6 @@ export interface DesktopAllVersionsNimbusExperiment {
187
188
  * Only used by Firefox Labs Opt-Ins.
188
189
  */
189
190
  requiresRestart?: boolean;
190
- localizations?: ExperimentLocalizations | null;
191
191
  }
192
192
  export interface ExperimentBucketConfig {
193
193
  randomizationUnit: RandomizationUnit;
@@ -206,7 +206,7 @@ export interface ExperimentBucketConfig {
206
206
  /**
207
207
  * The total number of buckets.
208
208
  *
209
- * You can assume this will always be 10000
209
+ * You can assume this will always be 10000.
210
210
  */
211
211
  total: number;
212
212
  }
@@ -220,6 +220,17 @@ export interface ExperimentOutcome {
220
220
  */
221
221
  priority: string;
222
222
  }
223
+ /**
224
+ * Per-locale localization substitutions.
225
+ *
226
+ * The top level key is the locale (e.g., "en-US" or "fr"). Each entry is a mapping of
227
+ * string IDs to their localized equivalents.
228
+ */
229
+ export interface ExperimentLocalizations {
230
+ [k: string]: {
231
+ [k: string]: string;
232
+ };
233
+ }
223
234
  /**
224
235
  * The branch definition supported on all Firefox Desktop versions.
225
236
  *
@@ -268,118 +279,6 @@ export interface DesktopPre95FeatureConfig {
268
279
  };
269
280
  enabled: false;
270
281
  }
271
- /**
272
- * Per-locale localization substitutions.
273
- *
274
- * The top level key is the locale (e.g., "en-US" or "fr"). Each entry is a mapping of
275
- * string IDs to their localized equivalents.
276
- */
277
- export interface ExperimentLocalizations {
278
- [k: string]: {
279
- [k: string]: string;
280
- };
281
- }
282
- /**
283
- * A feature.
284
- */
285
- export interface DesktopFeature {
286
- /**
287
- * The description of the feature.
288
- */
289
- description: string;
290
- /**
291
- * Whether or not this feature records exposure telemetry.
292
- */
293
- hasExposure: boolean;
294
- /**
295
- * A description of the exposure telemetry collected by this feature.
296
- *
297
- * Only required if hasExposure is true.
298
- */
299
- exposureDescription?: string;
300
- /**
301
- * The owner of the feature.
302
- */
303
- owner: string;
304
- /**
305
- * If true, the feature values will be cached in prefs so that they can be read before Nimbus is initialized during Firefox startup.
306
- */
307
- isEarlyStartup?: boolean;
308
- /**
309
- * The applications that can enroll in experiments for this feature.
310
- *
311
- * Defaults to "firefox-desktop".
312
- */
313
- applications?: DesktopApplication[];
314
- /**
315
- * The variables that this feature can set.
316
- */
317
- variables: {
318
- [k: string]: DesktopFeatureVariable;
319
- };
320
- schema?: NimbusFeatureSchema;
321
- }
322
- /**
323
- * A feature variable.
324
- */
325
- export interface DesktopFeatureVariable {
326
- /**
327
- * A description of the feature.
328
- */
329
- description: string;
330
- type: FeatureVariableType;
331
- /**
332
- * An optional list of possible string or integer values.
333
- *
334
- * Only allowed when type is string or int.
335
- *
336
- * The types in the enum must match the type of the field.
337
- */
338
- enum?: string[] | number[];
339
- /**
340
- * A pref that provides the default value for a feature when none is present.
341
- */
342
- fallbackPref?: string;
343
- /**
344
- * A pref that should be set to the value of this variable when enrolling in experiments.
345
- *
346
- * Using a string is deprecated and unsupported in Firefox 124+.
347
- */
348
- setPref?: string | SetPref;
349
- }
350
- export interface SetPref {
351
- branch: PrefBranch;
352
- /**
353
- * The name of the pref to set.
354
- */
355
- pref: string;
356
- }
357
- /**
358
- * Information about a JSON schema.
359
- */
360
- export interface NimbusFeatureSchema {
361
- /**
362
- * The resource:// or chrome:// URI that can be loaded at runtime within Firefox.
363
- *
364
- * Required by Firefox so that Nimbus can import the schema for validation.
365
- */
366
- uri: string;
367
- /**
368
- * The path to the schema file in the source checkout.
369
- *
370
- * Required by Experimenter so that it can find schema files in source checkouts.
371
- */
372
- path: string;
373
- }
374
- /**
375
- * The Firefox Desktop-specific feature manifest.
376
- *
377
- * Firefox Desktop requires different fields for its features compared to the general
378
- * Nimbus feature manifest.
379
- */
380
- export interface DesktopFeatureManifest {
381
- [k: string]: DesktopFeature;
382
- }
383
282
  /**
384
283
  * A Nimbus experiment for Firefox Desktop.
385
284
  *
@@ -447,7 +346,7 @@ export interface DesktopNimbusExperiment {
447
346
  /**
448
347
  * A list of featureIds the experiment contains configurations for.
449
348
  */
450
- featureIds?: string[];
349
+ featureIds: string[];
451
350
  /**
452
351
  * A JEXL targeting expression used to filter out experiments.
453
352
  */
@@ -492,6 +391,7 @@ export interface DesktopNimbusExperiment {
492
391
  * If null, all locales are targeted.
493
392
  */
494
393
  locales?: string[] | null;
394
+ localizations?: ExperimentLocalizations | null;
495
395
  /**
496
396
  * The date that this experiment was first published to Remote Settings.
497
397
  *
@@ -534,7 +434,6 @@ export interface DesktopNimbusExperiment {
534
434
  * Only used by Firefox Labs Opt-Ins.
535
435
  */
536
436
  requiresRestart?: boolean;
537
- localizations?: ExperimentLocalizations | null;
538
437
  }
539
438
  /**
540
439
  * The branch definition supported on Firefox Desktop 95+.
@@ -560,15 +459,154 @@ export interface DesktopExperimentBranch {
560
459
  firefoxLabsTitle?: string | null;
561
460
  }
562
461
  /**
563
- * The SDK-specific feature manifest.
462
+ * A Nimbus experiment for Nimbus SDK-based applications.
564
463
  */
565
- export interface SdkFeatureManifest {
566
- [k: string]: SdkFeature;
464
+ export interface SdkNimbusExperiment {
465
+ /**
466
+ * Version of the NimbusExperiment schema this experiment refers to
467
+ */
468
+ schemaVersion: string;
469
+ /**
470
+ * Unique identifier for the experiment
471
+ */
472
+ slug: string;
473
+ /**
474
+ * Unique identifier for the experiiment.
475
+ *
476
+ * This is a duplicate of slug, but is required field for all Remote Settings records.
477
+ */
478
+ id: string;
479
+ /**
480
+ * A slug identifying the targeted product of this experiment.
481
+ *
482
+ * It should be a lowercased_with_underscores name that is short and unambiguous and it should match the app_name found in https://probeinfo.telemetry.mozilla.org/glean/repositories. Examples are "fenix" and "firefox_desktop".
483
+ */
484
+ appName: string;
485
+ /**
486
+ * The platform identifier for the targeted app.
487
+ *
488
+ * This should match app's identifier exactly as it appears in the relevant app store listing (for relevant platforms) or the app's Glean initialization (for other platforms).
489
+ *
490
+ * Examples are "org.mozilla.firefox_beta" and "firefox-desktop".
491
+ */
492
+ appId: string;
493
+ /**
494
+ * A specific channel of an application such as "nightly", "beta", or "release".
495
+ */
496
+ channel: string;
497
+ /**
498
+ * Public name of the experiment that will be displayed on "about:studies".
499
+ */
500
+ userFacingName: string;
501
+ /**
502
+ * Short public description of the experiment that will be displayed on "about:studies".
503
+ */
504
+ userFacingDescription: string;
505
+ /**
506
+ * When this property is set to true, the SDK should not enroll new users into the experiment that have not already been enrolled.
507
+ */
508
+ isEnrollmentPaused: boolean;
509
+ /**
510
+ * When this property is set to true, treat this experiment as a rollout.
511
+ *
512
+ * Rollouts are currently handled as single-branch experiments separated from the bucketing namespace for normal experiments.
513
+ *
514
+ * See-also: https://mozilla-hub.atlassian.net/browse/SDK-405
515
+ */
516
+ isRollout?: boolean;
517
+ bucketConfig: ExperimentBucketConfig;
518
+ /**
519
+ * A list of outcomes relevant to the experiment analysis.
520
+ */
521
+ outcomes?: ExperimentOutcome[];
522
+ /**
523
+ * A list of featureIds the experiment contains configurations for.
524
+ */
525
+ featureIds: string[];
526
+ /**
527
+ * A JEXL targeting expression used to filter out experiments.
528
+ */
529
+ targeting?: string | null;
530
+ /**
531
+ * Actual publish date of the experiment.
532
+ *
533
+ * Note that this value is expected to be null in Remote Settings.
534
+ */
535
+ startDate: string | null;
536
+ /**
537
+ * Actual enrollment end date of the experiment.
538
+ *
539
+ * Note that this value is expected to be null in Remote Settings.
540
+ */
541
+ enrollmentEndDate?: string | null;
542
+ /**
543
+ * Actual end date of this experiment.
544
+ *
545
+ * Note that this field is expected to be null in Remote Settings.
546
+ */
547
+ endDate: string | null;
548
+ /**
549
+ * Duration of the experiment from the start date in days.
550
+ *
551
+ * Note that this property is only used during the analysis phase (i.e., not by the SDK).
552
+ */
553
+ proposedDuration?: number;
554
+ /**
555
+ * This represents the number of days that we expect to enroll new users.
556
+ *
557
+ * Note that this property is only used during the analysis phase (i.e., not by the SDK).
558
+ */
559
+ proposedEnrollment: number;
560
+ /**
561
+ * The slug of the reference branch (i.e., the branch we consider "control").
562
+ */
563
+ referenceBranch: string | null;
564
+ /**
565
+ * The list of locale codes (e.g., "en-US" or "fr") that this experiment is targeting.
566
+ *
567
+ * If null, all locales are targeted.
568
+ */
569
+ locales?: string[] | null;
570
+ /**
571
+ * Per-locale localization substitutions.
572
+ */
573
+ localizations?: ExperimentLocalizations | null;
574
+ /**
575
+ * The date that this experiment was first published to Remote Settings.
576
+ *
577
+ * If null, it has not yet been published.
578
+ */
579
+ publishedDate?: string | null;
580
+ /**
581
+ * Branch configuration for the SDK experiment.
582
+ */
583
+ branches: SdkExperimentBranch[];
584
+ }
585
+ /**
586
+ * The branch definition for SDK-based applications.
587
+ *
588
+ * Supported on Firefox for Android 96+, Firefox for iOS 39+, and all versions of Cirrus.
589
+ */
590
+ export interface SdkExperimentBranch {
591
+ /**
592
+ * Identifier for the branch.
593
+ */
594
+ slug: string;
595
+ /**
596
+ * Relative ratio of population for the branch.
597
+ *
598
+ * e.g., if branch A=1 and branch B=3, then branch A would get 25% of the population.
599
+ */
600
+ ratio: number;
601
+ /**
602
+ * An array of feature configurations.
603
+ */
604
+ features: ExperimentFeatureConfig[];
567
605
  }
568
606
  /**
569
607
  * A feature.
570
608
  */
571
- export interface SdkFeature {
609
+ export interface DesktopFeature {
572
610
  /**
573
611
  * The description of the feature.
574
612
  */
@@ -583,59 +621,143 @@ export interface SdkFeature {
583
621
  * Only required if hasExposure is true.
584
622
  */
585
623
  exposureDescription?: string;
624
+ /**
625
+ * The owner of the feature.
626
+ */
627
+ owner: string;
628
+ /**
629
+ * If true, the feature values will be cached in prefs so that they can be read before Nimbus is initialized during Firefox startup.
630
+ */
631
+ isEarlyStartup?: boolean;
632
+ /**
633
+ * The applications that can enroll in experiments for this feature.
634
+ *
635
+ * Defaults to "firefox-desktop".
636
+ */
637
+ applications?: DesktopApplication[];
586
638
  /**
587
639
  * The variables that this feature can set.
588
640
  */
589
641
  variables: {
590
- [k: string]: SdkFeatureVariable;
642
+ [k: string]: DesktopFeatureVariable;
591
643
  };
644
+ schema?: NimbusFeatureSchema;
645
+ /**
646
+ * If true, clients can enroll in multiple experiments and rollouts that use this feature.
647
+ */
648
+ allowCoenrollment?: boolean;
592
649
  }
593
650
  /**
594
651
  * A feature variable.
595
652
  */
596
- export interface SdkFeatureVariable {
653
+ export interface DesktopFeatureVariable {
597
654
  /**
598
655
  * A description of the feature.
599
656
  */
600
657
  description: string;
601
658
  type: FeatureVariableType;
602
659
  /**
603
- * An optional list of possible string values.
660
+ * An optional list of possible string or integer values.
604
661
  *
605
- * Only allowed when type is string.
662
+ * Only allowed when type is string or int.
663
+ *
664
+ * The types in the enum must match the type of the field.
606
665
  */
607
- enum?: string[];
666
+ enum?: string[] | number[];
667
+ /**
668
+ * A pref that provides the default value for a feature when none is present.
669
+ */
670
+ fallbackPref?: string;
671
+ /**
672
+ * A pref that should be set to the value of this variable when enrolling in experiments.
673
+ *
674
+ * Using a string is deprecated and unsupported in Firefox 124+.
675
+ */
676
+ setPref?: string | SetPref;
677
+ }
678
+ export interface SetPref {
679
+ branch: PrefBranch;
680
+ /**
681
+ * The name of the pref to set.
682
+ */
683
+ pref: string;
608
684
  }
609
685
  /**
610
- * A Nimbus experiment for Nimbus SDK-based applications.
686
+ * Information about a JSON schema.
611
687
  */
612
- export interface SdkNimbusExperiment {
688
+ export interface NimbusFeatureSchema {
613
689
  /**
614
- * Branch configuration for the experiment.
690
+ * The resource:// or chrome:// URI that can be loaded at runtime within Firefox.
691
+ *
692
+ * Required by Firefox so that Nimbus can import the schema for validation.
615
693
  */
616
- branches: SdkExperimentBranch[];
694
+ uri: string;
695
+ /**
696
+ * The path to the schema file in the source checkout.
697
+ *
698
+ * Required by Experimenter so that it can find schema files in source checkouts.
699
+ */
700
+ path: string;
617
701
  }
618
702
  /**
619
- * The branch definition for SDK-based applications
703
+ * The Firefox Desktop-specific feature manifest.
620
704
  *
621
- * Supported on Firefox for Android 96+ and Firefox for iOS 39+ and all versions of
622
- * Cirrus.
705
+ * Firefox Desktop requires different fields for its features compared to the general
706
+ * Nimbus feature manifest.
623
707
  */
624
- export interface SdkExperimentBranch {
708
+ export interface DesktopFeatureManifest {
709
+ [k: string]: DesktopFeature;
710
+ }
711
+ /**
712
+ * The SDK-specific feature manifest.
713
+ */
714
+ export interface SdkFeatureManifest {
715
+ [k: string]: SdkFeature;
716
+ }
717
+ /**
718
+ * A feature.
719
+ */
720
+ export interface SdkFeature {
625
721
  /**
626
- * Identifier for the branch.
722
+ * The description of the feature.
627
723
  */
628
- slug: string;
724
+ description: string;
629
725
  /**
630
- * Relative ratio of population for the branch.
726
+ * Whether or not this feature records exposure telemetry.
727
+ */
728
+ hasExposure: boolean;
729
+ /**
730
+ * A description of the exposure telemetry collected by this feature.
631
731
  *
632
- * e.g., if branch A=1 and branch B=3, then branch A would get 25% of the population.
732
+ * Only required if hasExposure is true.
633
733
  */
634
- ratio: number;
734
+ exposureDescription?: string;
635
735
  /**
636
- * An array of feature configurations.
736
+ * The variables that this feature can set.
637
737
  */
638
- features: ExperimentFeatureConfig[];
738
+ variables: {
739
+ [k: string]: SdkFeatureVariable;
740
+ };
741
+ /**
742
+ * If true, clients can enroll in multiple experiments and rollouts that use this feature.
743
+ */
744
+ "allow-coenrollment"?: boolean;
745
+ }
746
+ /**
747
+ * A feature variable.
748
+ */
749
+ export interface SdkFeatureVariable {
750
+ /**
751
+ * A description of the feature.
752
+ */
753
+ description: string;
754
+ type: FeatureVariableType;
755
+ /**
756
+ * An optional list of possible string values.
757
+ *
758
+ * Only allowed when type is string.
759
+ */
760
+ enum?: string[];
639
761
  }
640
762
  export interface AnalysisError {
641
763
  analysis_basis?: AnalysisBasis | null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mozilla/nimbus-schemas",
3
- "version": "2025.1.1",
3
+ "version": "3001.0.0",
4
4
  "description": "Schemas used by Mozilla Nimbus and related projects.",
5
5
  "main": "index.d.ts",
6
6
  "repository": {
@@ -142,6 +142,16 @@
142
142
  ],
143
143
  "description": "The list of locale codes (e.g., \"en-US\" or \"fr\") that this experiment is targeting. If null, all locales are targeted."
144
144
  },
145
+ "localizations": {
146
+ "anyOf": [
147
+ {
148
+ "$ref": "#/$defs/ExperimentLocalizations"
149
+ },
150
+ {
151
+ "type": "null"
152
+ }
153
+ ]
154
+ },
145
155
  "publishedDate": {
146
156
  "anyOf": [
147
157
  {
@@ -222,16 +232,6 @@
222
232
  "requiresRestart": {
223
233
  "description": "Does the experiment require a restart to take effect? Only used by Firefox Labs Opt-Ins.",
224
234
  "type": "boolean"
225
- },
226
- "localizations": {
227
- "anyOf": [
228
- {
229
- "$ref": "#/$defs/ExperimentLocalizations"
230
- },
231
- {
232
- "type": "null"
233
- }
234
- ]
235
235
  }
236
236
  },
237
237
  "required": [
@@ -245,6 +245,7 @@
245
245
  "userFacingDescription",
246
246
  "isEnrollmentPaused",
247
247
  "bucketConfig",
248
+ "featureIds",
248
249
  "startDate",
249
250
  "endDate",
250
251
  "proposedEnrollment",
@@ -323,7 +324,7 @@
323
324
  },
324
325
  "feature": {
325
326
  "$ref": "#/$defs/DesktopPre95FeatureConfig",
326
- "description": "The feature key must be provided with values to prevent crashes if the is encountered by Desktop clients earlier than version 95."
327
+ "description": "The feature key must be provided with values to prevent crashes if the experiment is encountered by Desktop clients earlier than version 95."
327
328
  }
328
329
  },
329
330
  "required": [
@@ -373,7 +374,7 @@
373
374
  "type": "integer"
374
375
  },
375
376
  "total": {
376
- "description": "The total number of buckets. You can assume this will always be 10000",
377
+ "description": "The total number of buckets. You can assume this will always be 10000.",
377
378
  "type": "integer"
378
379
  }
379
380
  },
@@ -431,7 +432,7 @@
431
432
  "type": "object"
432
433
  },
433
434
  "RandomizationUnit": {
434
- "description": "A unique, stable indentifier for the user used as an input to bucket hashing.",
435
+ "description": "A unique, stable identifier for the user used as an input to bucket hashing.",
435
436
  "enum": [
436
437
  "normandy_id",
437
438
  "nimbus_id",
@@ -42,6 +42,10 @@
42
42
  "schema": {
43
43
  "$ref": "#/$defs/NimbusFeatureSchema",
44
44
  "description": "An optional JSON schema that describes the feature variables."
45
+ },
46
+ "allowCoenrollment": {
47
+ "description": "If true, clients can enroll in multiple experiments and rollouts that use this feature.",
48
+ "type": "boolean"
45
49
  }
46
50
  },
47
51
  "required": [
@@ -62,6 +62,10 @@
62
62
  "schema": {
63
63
  "$ref": "#/$defs/NimbusFeatureSchema",
64
64
  "description": "An optional JSON schema that describes the feature variables."
65
+ },
66
+ "allowCoenrollment": {
67
+ "description": "If true, clients can enroll in multiple experiments and rollouts that use this feature.",
68
+ "type": "boolean"
65
69
  }
66
70
  },
67
71
  "required": [