@shipeasy/openapi 1.1.0 → 1.4.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/dist/index.js +49 -14
- package/dist/resources/experiments.d.ts +18 -30
- package/dist/resources/experiments.js +26 -8
- package/dist/resources/gates.d.ts +95 -0
- package/dist/resources/gates.js +23 -6
- package/dist/resources/index.d.ts +111 -30
- package/dist/resources/index.js +49 -14
- package/dist/schemas/experiments.d.ts +4 -20
- package/dist/schemas/experiments.js +7 -2
- package/dist/schemas/gates.d.ts +83 -1
- package/dist/schemas/gates.js +24 -6
- package/dist/schemas/index.d.ts +1 -1
- package/dist/schemas/index.js +31 -8
- package/openapi.json +1533 -236
- package/package.json +1 -1
package/dist/resources/gates.js
CHANGED
|
@@ -29,12 +29,14 @@ var gateRuleSchema = z2.object({
|
|
|
29
29
|
"semver_gt",
|
|
30
30
|
"semver_gte",
|
|
31
31
|
"semver_lt",
|
|
32
|
-
"semver_lte"
|
|
32
|
+
"semver_lte",
|
|
33
|
+
"gate_pass",
|
|
34
|
+
"exp_in"
|
|
33
35
|
]).describe(
|
|
34
|
-
|
|
36
|
+
'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 \u2014 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"` \u2014 true when the caller is in that variant / the universe holdout / any variant).'
|
|
35
37
|
),
|
|
36
38
|
value: z2.unknown().describe(
|
|
37
|
-
|
|
39
|
+
'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`.'
|
|
38
40
|
)
|
|
39
41
|
}).describe(
|
|
40
42
|
"Single targeting predicate. A gate's `rules` array is evaluated with AND semantics; use the gatekeeper `stack` for OR / fall-through logic."
|
|
@@ -45,9 +47,17 @@ var gateNameSchema = z2.string().regex(
|
|
|
45
47
|
).max(128).describe(
|
|
46
48
|
"Stable gate key used by SDKs (`Shipeasy.checkGate(user, '<name>')`). Single segment or `folder.name`. Lowercase letters, digits, `_` or `-`; max 128 chars. Immutable after create \u2014 rename = delete + recreate."
|
|
47
49
|
);
|
|
50
|
+
var rampSchema = z2.object({
|
|
51
|
+
from: z2.number().int().min(0).max(1e4).describe("Start of the ramp in basis points (0\u201310000)."),
|
|
52
|
+
to: z2.number().int().min(0).max(1e4).describe("End of the ramp in basis points (0\u201310000)."),
|
|
53
|
+
startAt: z2.number().int().nonnegative().describe("Ramp start, epoch milliseconds (UTC)."),
|
|
54
|
+
durationMs: z2.number().int().positive().describe("Ramp length in milliseconds.")
|
|
55
|
+
}).describe(
|
|
56
|
+
"Linear rollout ramp. The effective rollout % interpolates `from`\u2192`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."
|
|
57
|
+
);
|
|
48
58
|
var stackedConditionSchema = z2.object({
|
|
49
59
|
id: z2.string().min(1).describe(
|
|
50
|
-
"Client-supplied stable id for the stack entry (used for React keys, audit diffs)."
|
|
60
|
+
"Client-supplied stable id for the stack entry (used for React keys, audit diffs, and as the default per-condition rollout salt)."
|
|
51
61
|
),
|
|
52
62
|
type: z2.literal("condition").describe("Discriminator. `condition` = rule-based predicate sub-gate."),
|
|
53
63
|
name: z2.string().optional().describe("Human label shown in the dashboard."),
|
|
@@ -56,9 +66,15 @@ var stackedConditionSchema = z2.object({
|
|
|
56
66
|
),
|
|
57
67
|
pass: z2.enum(["all", "any"]).optional().describe("Combinator across `rules`. `all` = AND (default), `any` = OR."),
|
|
58
68
|
rules: z2.array(gateRuleSchema).default([]).describe("Predicates evaluated under `pass`. Empty array = match nothing."),
|
|
69
|
+
rolloutPct: z2.number().int().min(0).max(1e4).optional().describe(
|
|
70
|
+
"Per-condition rollout in basis points. After the rules match, the caller is bucketed at this %; in-bucket \u2192 gate `true`, otherwise fall through to the next entry. Absent \u21D2 10000 (100%, every matching caller passes)."
|
|
71
|
+
),
|
|
72
|
+
bucketBy: z2.string().optional().describe("Attribute used to hash the caller into the per-condition bucket. Default `userID`."),
|
|
73
|
+
salt: z2.string().optional().describe("Per-condition hash salt. Defaults to the entry `id`, then the gate salt."),
|
|
74
|
+
ramp: rampSchema.optional(),
|
|
59
75
|
locked: z2.boolean().optional().describe("If `true`, dashboard hides edit controls (e.g. the trailing public-floor entry).")
|
|
60
76
|
}).describe(
|
|
61
|
-
"Rule-based sub-gate. When
|
|
77
|
+
"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."
|
|
62
78
|
);
|
|
63
79
|
var stackedRolloutSchema = z2.object({
|
|
64
80
|
id: z2.string().min(1).describe("Client-supplied stable id for the stack entry."),
|
|
@@ -74,9 +90,10 @@ var stackedRolloutSchema = z2.object({
|
|
|
74
90
|
salt: z2.string().optional().describe(
|
|
75
91
|
"Per-entry hash salt. Defaults to the gate's salt. Set explicitly to keep two rollouts independent or correlated across gates."
|
|
76
92
|
),
|
|
93
|
+
ramp: rampSchema.optional(),
|
|
77
94
|
locked: z2.boolean().optional().describe("If `true`, dashboard hides edit controls.")
|
|
78
95
|
}).describe(
|
|
79
|
-
"Percentage-rollout sub-gate. Hashes `bucketBy` (default `userID`) with `salt` and returns `true` for `rolloutPct/10000` fraction of callers."
|
|
96
|
+
"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)."
|
|
80
97
|
);
|
|
81
98
|
var stackedGateEntrySchema = z2.discriminatedUnion("type", [stackedConditionSchema, stackedRolloutSchema]).describe(
|
|
82
99
|
"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."
|
|
@@ -80,6 +80,8 @@ declare const RESOURCE_REGISTRY: readonly [{
|
|
|
80
80
|
semver_gte: "semver_gte";
|
|
81
81
|
semver_lt: "semver_lt";
|
|
82
82
|
semver_lte: "semver_lte";
|
|
83
|
+
gate_pass: "gate_pass";
|
|
84
|
+
exp_in: "exp_in";
|
|
83
85
|
}>;
|
|
84
86
|
value: zod.ZodUnknown;
|
|
85
87
|
}, zod_v4_core.$strip>>>;
|
|
@@ -110,9 +112,20 @@ declare const RESOURCE_REGISTRY: readonly [{
|
|
|
110
112
|
semver_gte: "semver_gte";
|
|
111
113
|
semver_lt: "semver_lt";
|
|
112
114
|
semver_lte: "semver_lte";
|
|
115
|
+
gate_pass: "gate_pass";
|
|
116
|
+
exp_in: "exp_in";
|
|
113
117
|
}>;
|
|
114
118
|
value: zod.ZodUnknown;
|
|
115
119
|
}, zod_v4_core.$strip>>>;
|
|
120
|
+
rolloutPct: zod.ZodOptional<zod.ZodNumber>;
|
|
121
|
+
bucketBy: zod.ZodOptional<zod.ZodString>;
|
|
122
|
+
salt: zod.ZodOptional<zod.ZodString>;
|
|
123
|
+
ramp: zod.ZodOptional<zod.ZodObject<{
|
|
124
|
+
from: zod.ZodNumber;
|
|
125
|
+
to: zod.ZodNumber;
|
|
126
|
+
startAt: zod.ZodNumber;
|
|
127
|
+
durationMs: zod.ZodNumber;
|
|
128
|
+
}, zod_v4_core.$strip>>;
|
|
116
129
|
locked: zod.ZodOptional<zod.ZodBoolean>;
|
|
117
130
|
}, zod_v4_core.$strip>, zod.ZodObject<{
|
|
118
131
|
id: zod.ZodString;
|
|
@@ -122,6 +135,12 @@ declare const RESOURCE_REGISTRY: readonly [{
|
|
|
122
135
|
rolloutPct: zod.ZodNumber;
|
|
123
136
|
bucketBy: zod.ZodOptional<zod.ZodString>;
|
|
124
137
|
salt: zod.ZodOptional<zod.ZodString>;
|
|
138
|
+
ramp: zod.ZodOptional<zod.ZodObject<{
|
|
139
|
+
from: zod.ZodNumber;
|
|
140
|
+
to: zod.ZodNumber;
|
|
141
|
+
startAt: zod.ZodNumber;
|
|
142
|
+
durationMs: zod.ZodNumber;
|
|
143
|
+
}, zod_v4_core.$strip>>;
|
|
125
144
|
locked: zod.ZodOptional<zod.ZodBoolean>;
|
|
126
145
|
}, zod_v4_core.$strip>], "type">>>>;
|
|
127
146
|
}, zod_v4_core.$strip>;
|
|
@@ -150,6 +169,8 @@ declare const RESOURCE_REGISTRY: readonly [{
|
|
|
150
169
|
semver_gte: "semver_gte";
|
|
151
170
|
semver_lt: "semver_lt";
|
|
152
171
|
semver_lte: "semver_lte";
|
|
172
|
+
gate_pass: "gate_pass";
|
|
173
|
+
exp_in: "exp_in";
|
|
153
174
|
}>;
|
|
154
175
|
value: zod.ZodUnknown;
|
|
155
176
|
}, zod_v4_core.$strip>>>;
|
|
@@ -180,9 +201,20 @@ declare const RESOURCE_REGISTRY: readonly [{
|
|
|
180
201
|
semver_gte: "semver_gte";
|
|
181
202
|
semver_lt: "semver_lt";
|
|
182
203
|
semver_lte: "semver_lte";
|
|
204
|
+
gate_pass: "gate_pass";
|
|
205
|
+
exp_in: "exp_in";
|
|
183
206
|
}>;
|
|
184
207
|
value: zod.ZodUnknown;
|
|
185
208
|
}, zod_v4_core.$strip>>>;
|
|
209
|
+
rolloutPct: zod.ZodOptional<zod.ZodNumber>;
|
|
210
|
+
bucketBy: zod.ZodOptional<zod.ZodString>;
|
|
211
|
+
salt: zod.ZodOptional<zod.ZodString>;
|
|
212
|
+
ramp: zod.ZodOptional<zod.ZodObject<{
|
|
213
|
+
from: zod.ZodNumber;
|
|
214
|
+
to: zod.ZodNumber;
|
|
215
|
+
startAt: zod.ZodNumber;
|
|
216
|
+
durationMs: zod.ZodNumber;
|
|
217
|
+
}, zod_v4_core.$strip>>;
|
|
186
218
|
locked: zod.ZodOptional<zod.ZodBoolean>;
|
|
187
219
|
}, zod_v4_core.$strip>, zod.ZodObject<{
|
|
188
220
|
id: zod.ZodString;
|
|
@@ -192,6 +224,12 @@ declare const RESOURCE_REGISTRY: readonly [{
|
|
|
192
224
|
rolloutPct: zod.ZodNumber;
|
|
193
225
|
bucketBy: zod.ZodOptional<zod.ZodString>;
|
|
194
226
|
salt: zod.ZodOptional<zod.ZodString>;
|
|
227
|
+
ramp: zod.ZodOptional<zod.ZodObject<{
|
|
228
|
+
from: zod.ZodNumber;
|
|
229
|
+
to: zod.ZodNumber;
|
|
230
|
+
startAt: zod.ZodNumber;
|
|
231
|
+
durationMs: zod.ZodNumber;
|
|
232
|
+
}, zod_v4_core.$strip>>;
|
|
195
233
|
locked: zod.ZodOptional<zod.ZodBoolean>;
|
|
196
234
|
}, zod_v4_core.$strip>], "type">>>>;
|
|
197
235
|
}, zod_v4_core.$strip>;
|
|
@@ -232,6 +270,8 @@ declare const RESOURCE_REGISTRY: readonly [{
|
|
|
232
270
|
semver_gte: "semver_gte";
|
|
233
271
|
semver_lt: "semver_lt";
|
|
234
272
|
semver_lte: "semver_lte";
|
|
273
|
+
gate_pass: "gate_pass";
|
|
274
|
+
exp_in: "exp_in";
|
|
235
275
|
}>;
|
|
236
276
|
value: zod.ZodUnknown;
|
|
237
277
|
}, zod_v4_core.$strip>>>;
|
|
@@ -267,9 +307,20 @@ declare const RESOURCE_REGISTRY: readonly [{
|
|
|
267
307
|
semver_gte: "semver_gte";
|
|
268
308
|
semver_lt: "semver_lt";
|
|
269
309
|
semver_lte: "semver_lte";
|
|
310
|
+
gate_pass: "gate_pass";
|
|
311
|
+
exp_in: "exp_in";
|
|
270
312
|
}>;
|
|
271
313
|
value: zod.ZodUnknown;
|
|
272
314
|
}, zod_v4_core.$strip>>>;
|
|
315
|
+
rolloutPct: zod.ZodOptional<zod.ZodNumber>;
|
|
316
|
+
bucketBy: zod.ZodOptional<zod.ZodString>;
|
|
317
|
+
salt: zod.ZodOptional<zod.ZodString>;
|
|
318
|
+
ramp: zod.ZodOptional<zod.ZodObject<{
|
|
319
|
+
from: zod.ZodNumber;
|
|
320
|
+
to: zod.ZodNumber;
|
|
321
|
+
startAt: zod.ZodNumber;
|
|
322
|
+
durationMs: zod.ZodNumber;
|
|
323
|
+
}, zod_v4_core.$strip>>;
|
|
273
324
|
locked: zod.ZodOptional<zod.ZodBoolean>;
|
|
274
325
|
}, zod_v4_core.$strip>, zod.ZodObject<{
|
|
275
326
|
id: zod.ZodString;
|
|
@@ -279,6 +330,12 @@ declare const RESOURCE_REGISTRY: readonly [{
|
|
|
279
330
|
rolloutPct: zod.ZodNumber;
|
|
280
331
|
bucketBy: zod.ZodOptional<zod.ZodString>;
|
|
281
332
|
salt: zod.ZodOptional<zod.ZodString>;
|
|
333
|
+
ramp: zod.ZodOptional<zod.ZodObject<{
|
|
334
|
+
from: zod.ZodNumber;
|
|
335
|
+
to: zod.ZodNumber;
|
|
336
|
+
startAt: zod.ZodNumber;
|
|
337
|
+
durationMs: zod.ZodNumber;
|
|
338
|
+
}, zod_v4_core.$strip>>;
|
|
282
339
|
locked: zod.ZodOptional<zod.ZodBoolean>;
|
|
283
340
|
}, zod_v4_core.$strip>], "type">>>>;
|
|
284
341
|
updatedAt: zod.ZodString;
|
|
@@ -348,6 +405,8 @@ declare const RESOURCE_REGISTRY: readonly [{
|
|
|
348
405
|
semver_gte: "semver_gte";
|
|
349
406
|
semver_lt: "semver_lt";
|
|
350
407
|
semver_lte: "semver_lte";
|
|
408
|
+
gate_pass: "gate_pass";
|
|
409
|
+
exp_in: "exp_in";
|
|
351
410
|
}>;
|
|
352
411
|
value: zod.ZodUnknown;
|
|
353
412
|
}, zod_v4_core.$strip>>>;
|
|
@@ -378,9 +437,20 @@ declare const RESOURCE_REGISTRY: readonly [{
|
|
|
378
437
|
semver_gte: "semver_gte";
|
|
379
438
|
semver_lt: "semver_lt";
|
|
380
439
|
semver_lte: "semver_lte";
|
|
440
|
+
gate_pass: "gate_pass";
|
|
441
|
+
exp_in: "exp_in";
|
|
381
442
|
}>;
|
|
382
443
|
value: zod.ZodUnknown;
|
|
383
444
|
}, zod_v4_core.$strip>>>;
|
|
445
|
+
rolloutPct: zod.ZodOptional<zod.ZodNumber>;
|
|
446
|
+
bucketBy: zod.ZodOptional<zod.ZodString>;
|
|
447
|
+
salt: zod.ZodOptional<zod.ZodString>;
|
|
448
|
+
ramp: zod.ZodOptional<zod.ZodObject<{
|
|
449
|
+
from: zod.ZodNumber;
|
|
450
|
+
to: zod.ZodNumber;
|
|
451
|
+
startAt: zod.ZodNumber;
|
|
452
|
+
durationMs: zod.ZodNumber;
|
|
453
|
+
}, zod_v4_core.$strip>>;
|
|
384
454
|
locked: zod.ZodOptional<zod.ZodBoolean>;
|
|
385
455
|
}, zod_v4_core.$strip>, zod.ZodObject<{
|
|
386
456
|
id: zod.ZodString;
|
|
@@ -390,6 +460,12 @@ declare const RESOURCE_REGISTRY: readonly [{
|
|
|
390
460
|
rolloutPct: zod.ZodNumber;
|
|
391
461
|
bucketBy: zod.ZodOptional<zod.ZodString>;
|
|
392
462
|
salt: zod.ZodOptional<zod.ZodString>;
|
|
463
|
+
ramp: zod.ZodOptional<zod.ZodObject<{
|
|
464
|
+
from: zod.ZodNumber;
|
|
465
|
+
to: zod.ZodNumber;
|
|
466
|
+
startAt: zod.ZodNumber;
|
|
467
|
+
durationMs: zod.ZodNumber;
|
|
468
|
+
}, zod_v4_core.$strip>>;
|
|
393
469
|
locked: zod.ZodOptional<zod.ZodBoolean>;
|
|
394
470
|
}, zod_v4_core.$strip>], "type">>>>;
|
|
395
471
|
}, zod_v4_core.$strip>;
|
|
@@ -515,6 +591,8 @@ declare const RESOURCE_REGISTRY: readonly [{
|
|
|
515
591
|
semver_gte: "semver_gte";
|
|
516
592
|
semver_lt: "semver_lt";
|
|
517
593
|
semver_lte: "semver_lte";
|
|
594
|
+
gate_pass: "gate_pass";
|
|
595
|
+
exp_in: "exp_in";
|
|
518
596
|
}>;
|
|
519
597
|
value: zod.ZodUnknown;
|
|
520
598
|
}, zod_v4_core.$strip>>>;
|
|
@@ -545,9 +623,20 @@ declare const RESOURCE_REGISTRY: readonly [{
|
|
|
545
623
|
semver_gte: "semver_gte";
|
|
546
624
|
semver_lt: "semver_lt";
|
|
547
625
|
semver_lte: "semver_lte";
|
|
626
|
+
gate_pass: "gate_pass";
|
|
627
|
+
exp_in: "exp_in";
|
|
548
628
|
}>;
|
|
549
629
|
value: zod.ZodUnknown;
|
|
550
630
|
}, zod_v4_core.$strip>>>;
|
|
631
|
+
rolloutPct: zod.ZodOptional<zod.ZodNumber>;
|
|
632
|
+
bucketBy: zod.ZodOptional<zod.ZodString>;
|
|
633
|
+
salt: zod.ZodOptional<zod.ZodString>;
|
|
634
|
+
ramp: zod.ZodOptional<zod.ZodObject<{
|
|
635
|
+
from: zod.ZodNumber;
|
|
636
|
+
to: zod.ZodNumber;
|
|
637
|
+
startAt: zod.ZodNumber;
|
|
638
|
+
durationMs: zod.ZodNumber;
|
|
639
|
+
}, zod_v4_core.$strip>>;
|
|
551
640
|
locked: zod.ZodOptional<zod.ZodBoolean>;
|
|
552
641
|
}, zod_v4_core.$strip>, zod.ZodObject<{
|
|
553
642
|
id: zod.ZodString;
|
|
@@ -557,6 +646,12 @@ declare const RESOURCE_REGISTRY: readonly [{
|
|
|
557
646
|
rolloutPct: zod.ZodNumber;
|
|
558
647
|
bucketBy: zod.ZodOptional<zod.ZodString>;
|
|
559
648
|
salt: zod.ZodOptional<zod.ZodString>;
|
|
649
|
+
ramp: zod.ZodOptional<zod.ZodObject<{
|
|
650
|
+
from: zod.ZodNumber;
|
|
651
|
+
to: zod.ZodNumber;
|
|
652
|
+
startAt: zod.ZodNumber;
|
|
653
|
+
durationMs: zod.ZodNumber;
|
|
654
|
+
}, zod_v4_core.$strip>>;
|
|
560
655
|
locked: zod.ZodOptional<zod.ZodBoolean>;
|
|
561
656
|
}, zod_v4_core.$strip>], "type">>>>;
|
|
562
657
|
}, zod_v4_core.$strip>;
|
|
@@ -709,11 +804,7 @@ declare const RESOURCE_REGISTRY: readonly [{
|
|
|
709
804
|
tag: zod.ZodDefault<zod.ZodNullable<zod.ZodString>>;
|
|
710
805
|
owner_email: zod.ZodDefault<zod.ZodNullable<zod.ZodString>>;
|
|
711
806
|
audience: zod.ZodDefault<zod.ZodNullable<zod.ZodString>>;
|
|
712
|
-
bucket_by: zod.ZodDefault<zod.ZodNullable<zod.
|
|
713
|
-
user_id: "user_id";
|
|
714
|
-
session_id: "session_id";
|
|
715
|
-
device_id: "device_id";
|
|
716
|
-
}>>>;
|
|
807
|
+
bucket_by: zod.ZodDefault<zod.ZodNullable<zod.ZodString>>;
|
|
717
808
|
folder: zod.ZodOptional<zod.ZodNullable<zod.ZodString>>;
|
|
718
809
|
universe: zod.ZodString;
|
|
719
810
|
targeting_gate: zod.ZodDefault<zod.ZodNullable<zod.ZodString>>;
|
|
@@ -749,11 +840,7 @@ declare const RESOURCE_REGISTRY: readonly [{
|
|
|
749
840
|
tag: zod.ZodOptional<zod.ZodNullable<zod.ZodString>>;
|
|
750
841
|
owner_email: zod.ZodOptional<zod.ZodNullable<zod.ZodString>>;
|
|
751
842
|
audience: zod.ZodOptional<zod.ZodNullable<zod.ZodString>>;
|
|
752
|
-
bucket_by: zod.ZodOptional<zod.ZodNullable<zod.
|
|
753
|
-
user_id: "user_id";
|
|
754
|
-
session_id: "session_id";
|
|
755
|
-
device_id: "device_id";
|
|
756
|
-
}>>>;
|
|
843
|
+
bucket_by: zod.ZodOptional<zod.ZodNullable<zod.ZodString>>;
|
|
757
844
|
folder: zod.ZodOptional<zod.ZodNullable<zod.ZodString>>;
|
|
758
845
|
targeting_gate: zod.ZodOptional<zod.ZodNullable<zod.ZodString>>;
|
|
759
846
|
allocation_pct: zod.ZodOptional<zod.ZodNumber>;
|
|
@@ -792,6 +879,9 @@ declare const RESOURCE_REGISTRY: readonly [{
|
|
|
792
879
|
}, {
|
|
793
880
|
readonly name: "archive";
|
|
794
881
|
readonly description: "Archive a stopped experiment";
|
|
882
|
+
}, {
|
|
883
|
+
readonly name: "restore";
|
|
884
|
+
readonly description: "Restore an archived (never-started) experiment to draft";
|
|
795
885
|
}, {
|
|
796
886
|
readonly name: "reanalyze";
|
|
797
887
|
readonly description: "Re-run analysis pass for the experiment";
|
|
@@ -811,11 +901,7 @@ declare const RESOURCE_REGISTRY: readonly [{
|
|
|
811
901
|
tag: zod.ZodNullable<zod.ZodString>;
|
|
812
902
|
ownerEmail: zod.ZodNullable<zod.ZodString>;
|
|
813
903
|
audience: zod.ZodNullable<zod.ZodString>;
|
|
814
|
-
bucketBy: zod.ZodNullable<zod.
|
|
815
|
-
user_id: "user_id";
|
|
816
|
-
session_id: "session_id";
|
|
817
|
-
device_id: "device_id";
|
|
818
|
-
}>>;
|
|
904
|
+
bucketBy: zod.ZodNullable<zod.ZodString>;
|
|
819
905
|
folder: zod.ZodNullable<zod.ZodString>;
|
|
820
906
|
status: zod.ZodEnum<{
|
|
821
907
|
draft: "draft";
|
|
@@ -898,11 +984,7 @@ declare const RESOURCE_REGISTRY: readonly [{
|
|
|
898
984
|
tag: zod.ZodNullable<zod.ZodString>;
|
|
899
985
|
ownerEmail: zod.ZodNullable<zod.ZodString>;
|
|
900
986
|
audience: zod.ZodNullable<zod.ZodString>;
|
|
901
|
-
bucketBy: zod.ZodNullable<zod.
|
|
902
|
-
user_id: "user_id";
|
|
903
|
-
session_id: "session_id";
|
|
904
|
-
device_id: "device_id";
|
|
905
|
-
}>>;
|
|
987
|
+
bucketBy: zod.ZodNullable<zod.ZodString>;
|
|
906
988
|
folder: zod.ZodNullable<zod.ZodString>;
|
|
907
989
|
status: zod.ZodEnum<{
|
|
908
990
|
draft: "draft";
|
|
@@ -977,11 +1059,7 @@ declare const RESOURCE_REGISTRY: readonly [{
|
|
|
977
1059
|
tag: zod.ZodDefault<zod.ZodNullable<zod.ZodString>>;
|
|
978
1060
|
owner_email: zod.ZodDefault<zod.ZodNullable<zod.ZodString>>;
|
|
979
1061
|
audience: zod.ZodDefault<zod.ZodNullable<zod.ZodString>>;
|
|
980
|
-
bucket_by: zod.ZodDefault<zod.ZodNullable<zod.
|
|
981
|
-
user_id: "user_id";
|
|
982
|
-
session_id: "session_id";
|
|
983
|
-
device_id: "device_id";
|
|
984
|
-
}>>>;
|
|
1062
|
+
bucket_by: zod.ZodDefault<zod.ZodNullable<zod.ZodString>>;
|
|
985
1063
|
folder: zod.ZodOptional<zod.ZodNullable<zod.ZodString>>;
|
|
986
1064
|
universe: zod.ZodString;
|
|
987
1065
|
targeting_gate: zod.ZodDefault<zod.ZodNullable<zod.ZodString>>;
|
|
@@ -1116,11 +1194,7 @@ declare const RESOURCE_REGISTRY: readonly [{
|
|
|
1116
1194
|
tag: zod.ZodOptional<zod.ZodNullable<zod.ZodString>>;
|
|
1117
1195
|
owner_email: zod.ZodOptional<zod.ZodNullable<zod.ZodString>>;
|
|
1118
1196
|
audience: zod.ZodOptional<zod.ZodNullable<zod.ZodString>>;
|
|
1119
|
-
bucket_by: zod.ZodOptional<zod.ZodNullable<zod.
|
|
1120
|
-
user_id: "user_id";
|
|
1121
|
-
session_id: "session_id";
|
|
1122
|
-
device_id: "device_id";
|
|
1123
|
-
}>>>;
|
|
1197
|
+
bucket_by: zod.ZodOptional<zod.ZodNullable<zod.ZodString>>;
|
|
1124
1198
|
folder: zod.ZodOptional<zod.ZodNullable<zod.ZodString>>;
|
|
1125
1199
|
targeting_gate: zod.ZodOptional<zod.ZodNullable<zod.ZodString>>;
|
|
1126
1200
|
allocation_pct: zod.ZodOptional<zod.ZodNumber>;
|
|
@@ -1262,6 +1336,13 @@ declare const RESOURCE_REGISTRY: readonly [{
|
|
|
1262
1336
|
readonly status: "archived";
|
|
1263
1337
|
};
|
|
1264
1338
|
};
|
|
1339
|
+
readonly restore: {
|
|
1340
|
+
readonly summary: "Restore — archived → draft";
|
|
1341
|
+
readonly description: "Un-deletes a soft-deleted experiment so it can be re-completed and started. Allowed only if it never ran.";
|
|
1342
|
+
readonly value: {
|
|
1343
|
+
readonly status: "draft";
|
|
1344
|
+
};
|
|
1345
|
+
};
|
|
1265
1346
|
};
|
|
1266
1347
|
readonly response: {
|
|
1267
1348
|
readonly id: "exp_01j7wb12c3d4e5f6g7h8j9k0l1";
|
package/dist/resources/index.js
CHANGED
|
@@ -29,12 +29,14 @@ var gateRuleSchema = z2.object({
|
|
|
29
29
|
"semver_gt",
|
|
30
30
|
"semver_gte",
|
|
31
31
|
"semver_lt",
|
|
32
|
-
"semver_lte"
|
|
32
|
+
"semver_lte",
|
|
33
|
+
"gate_pass",
|
|
34
|
+
"exp_in"
|
|
33
35
|
]).describe(
|
|
34
|
-
|
|
36
|
+
'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 \u2014 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"` \u2014 true when the caller is in that variant / the universe holdout / any variant).'
|
|
35
37
|
),
|
|
36
38
|
value: z2.unknown().describe(
|
|
37
|
-
|
|
39
|
+
'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`.'
|
|
38
40
|
)
|
|
39
41
|
}).describe(
|
|
40
42
|
"Single targeting predicate. A gate's `rules` array is evaluated with AND semantics; use the gatekeeper `stack` for OR / fall-through logic."
|
|
@@ -45,9 +47,17 @@ var gateNameSchema = z2.string().regex(
|
|
|
45
47
|
).max(128).describe(
|
|
46
48
|
"Stable gate key used by SDKs (`Shipeasy.checkGate(user, '<name>')`). Single segment or `folder.name`. Lowercase letters, digits, `_` or `-`; max 128 chars. Immutable after create \u2014 rename = delete + recreate."
|
|
47
49
|
);
|
|
50
|
+
var rampSchema = z2.object({
|
|
51
|
+
from: z2.number().int().min(0).max(1e4).describe("Start of the ramp in basis points (0\u201310000)."),
|
|
52
|
+
to: z2.number().int().min(0).max(1e4).describe("End of the ramp in basis points (0\u201310000)."),
|
|
53
|
+
startAt: z2.number().int().nonnegative().describe("Ramp start, epoch milliseconds (UTC)."),
|
|
54
|
+
durationMs: z2.number().int().positive().describe("Ramp length in milliseconds.")
|
|
55
|
+
}).describe(
|
|
56
|
+
"Linear rollout ramp. The effective rollout % interpolates `from`\u2192`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."
|
|
57
|
+
);
|
|
48
58
|
var stackedConditionSchema = z2.object({
|
|
49
59
|
id: z2.string().min(1).describe(
|
|
50
|
-
"Client-supplied stable id for the stack entry (used for React keys, audit diffs)."
|
|
60
|
+
"Client-supplied stable id for the stack entry (used for React keys, audit diffs, and as the default per-condition rollout salt)."
|
|
51
61
|
),
|
|
52
62
|
type: z2.literal("condition").describe("Discriminator. `condition` = rule-based predicate sub-gate."),
|
|
53
63
|
name: z2.string().optional().describe("Human label shown in the dashboard."),
|
|
@@ -56,9 +66,15 @@ var stackedConditionSchema = z2.object({
|
|
|
56
66
|
),
|
|
57
67
|
pass: z2.enum(["all", "any"]).optional().describe("Combinator across `rules`. `all` = AND (default), `any` = OR."),
|
|
58
68
|
rules: z2.array(gateRuleSchema).default([]).describe("Predicates evaluated under `pass`. Empty array = match nothing."),
|
|
69
|
+
rolloutPct: z2.number().int().min(0).max(1e4).optional().describe(
|
|
70
|
+
"Per-condition rollout in basis points. After the rules match, the caller is bucketed at this %; in-bucket \u2192 gate `true`, otherwise fall through to the next entry. Absent \u21D2 10000 (100%, every matching caller passes)."
|
|
71
|
+
),
|
|
72
|
+
bucketBy: z2.string().optional().describe("Attribute used to hash the caller into the per-condition bucket. Default `userID`."),
|
|
73
|
+
salt: z2.string().optional().describe("Per-condition hash salt. Defaults to the entry `id`, then the gate salt."),
|
|
74
|
+
ramp: rampSchema.optional(),
|
|
59
75
|
locked: z2.boolean().optional().describe("If `true`, dashboard hides edit controls (e.g. the trailing public-floor entry).")
|
|
60
76
|
}).describe(
|
|
61
|
-
"Rule-based sub-gate. When
|
|
77
|
+
"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."
|
|
62
78
|
);
|
|
63
79
|
var stackedRolloutSchema = z2.object({
|
|
64
80
|
id: z2.string().min(1).describe("Client-supplied stable id for the stack entry."),
|
|
@@ -74,9 +90,10 @@ var stackedRolloutSchema = z2.object({
|
|
|
74
90
|
salt: z2.string().optional().describe(
|
|
75
91
|
"Per-entry hash salt. Defaults to the gate's salt. Set explicitly to keep two rollouts independent or correlated across gates."
|
|
76
92
|
),
|
|
93
|
+
ramp: rampSchema.optional(),
|
|
77
94
|
locked: z2.boolean().optional().describe("If `true`, dashboard hides edit controls.")
|
|
78
95
|
}).describe(
|
|
79
|
-
"Percentage-rollout sub-gate. Hashes `bucketBy` (default `userID`) with `salt` and returns `true` for `rolloutPct/10000` fraction of callers."
|
|
96
|
+
"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)."
|
|
80
97
|
);
|
|
81
98
|
var stackedGateEntrySchema = z2.discriminatedUnion("type", [stackedConditionSchema, stackedRolloutSchema]).describe(
|
|
82
99
|
"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."
|
|
@@ -530,7 +547,12 @@ var experimentGroupSchema = z5.object({
|
|
|
530
547
|
var paramSchemaSchema = z5.record(z5.string(), z5.enum(["string", "bool", "number"])).describe(
|
|
531
548
|
"Map of param-name \u2192 scalar type. Defines the shape of `groups[].params`. Example: `{ headline: 'string', show_cta: 'bool' }`."
|
|
532
549
|
);
|
|
533
|
-
var bucketBySchema = z5.
|
|
550
|
+
var bucketBySchema = z5.string().min(1).max(128).regex(
|
|
551
|
+
/^[a-zA-Z_][a-zA-Z0-9_]*$/,
|
|
552
|
+
"Must be a user-attribute name (letters, digits, underscore; not starting with a digit)."
|
|
553
|
+
).describe(
|
|
554
|
+
"User-attribute name used as the bucketing key \u2014 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."
|
|
555
|
+
);
|
|
534
556
|
var experimentCreateSchema = z5.object({
|
|
535
557
|
name: experimentNameSchema,
|
|
536
558
|
description: z5.string().max(2e3).nullable().default(null).describe("Free-form description. Max 2000 chars, markdown rendered in the dashboard."),
|
|
@@ -602,7 +624,7 @@ var experimentUpdateSchema = z5.object({
|
|
|
602
624
|
);
|
|
603
625
|
var experimentStatusUpdateSchema = z5.object({
|
|
604
626
|
status: z5.enum(["draft", "running", "stopped", "archived"]).describe(
|
|
605
|
-
"Target status. Allowed transitions: `draft \u2192 running`, `running \u2192 stopped`, `stopped \u2192 archived`, `draft \u2192 archived
|
|
627
|
+
"Target status. Allowed transitions: `draft \u2192 running`, `running \u2192 stopped`, `stopped \u2192 archived`, `draft \u2192 archived`, and `archived \u2192 draft` (restore \u2014 only if the experiment never started). Restarting an archived experiment directly is not allowed; restore it to draft first, then start."
|
|
606
628
|
)
|
|
607
629
|
}).describe("Body for `POST /api/admin/experiments/{id}/status`.");
|
|
608
630
|
var experimentMetricsUpdateSchema = z5.object({
|
|
@@ -707,24 +729,29 @@ function experimentsClient(t) {
|
|
|
707
729
|
if (opts.cursor) query.cursor = opts.cursor;
|
|
708
730
|
return t.request("GET", BASE2, void 0, query);
|
|
709
731
|
}
|
|
710
|
-
async function
|
|
732
|
+
async function listAllWith(status) {
|
|
711
733
|
const out = [];
|
|
712
734
|
let cursor;
|
|
713
735
|
do {
|
|
714
|
-
const
|
|
736
|
+
const query = { limit: "500" };
|
|
737
|
+
if (cursor) query.cursor = cursor;
|
|
738
|
+
if (status) query.status = status;
|
|
739
|
+
const page = await t.request("GET", BASE2, void 0, query);
|
|
715
740
|
out.push(...page.data);
|
|
716
741
|
cursor = page.next_cursor ?? void 0;
|
|
717
742
|
} while (cursor);
|
|
718
743
|
return out;
|
|
719
744
|
}
|
|
745
|
+
async function listAll() {
|
|
746
|
+
return listAllWith();
|
|
747
|
+
}
|
|
720
748
|
async function resolve(idOrName) {
|
|
721
749
|
try {
|
|
722
750
|
return await t.request("GET", `${BASE2}/${idOrName}`);
|
|
723
751
|
} catch (err) {
|
|
724
752
|
if (!(err instanceof ApiError) || err.status !== 404) throw err;
|
|
725
753
|
}
|
|
726
|
-
const
|
|
727
|
-
const found = all.find((e) => e.name === idOrName);
|
|
754
|
+
const found = (await listAllWith()).find((e) => e.name === idOrName) ?? (await listAllWith("archived")).find((e) => e.name === idOrName);
|
|
728
755
|
if (!found) throw new ApiError(`Experiment '${idOrName}' not found`, 404);
|
|
729
756
|
return found;
|
|
730
757
|
}
|
|
@@ -747,6 +774,7 @@ function experimentsClient(t) {
|
|
|
747
774
|
start: (id) => setStatus(id, "running"),
|
|
748
775
|
stop: (id) => setStatus(id, "stopped"),
|
|
749
776
|
archive: (id) => setStatus(id, "archived"),
|
|
777
|
+
restore: (id) => setStatus(id, "draft"),
|
|
750
778
|
setMetrics: (id, input) => t.request("POST", `${BASE2}/${id}/metrics`, experimentMetricsUpdateSchema.parse(input)),
|
|
751
779
|
results: (id) => t.request("GET", `${BASE2}/${id}/results`),
|
|
752
780
|
timeseries: (id, metric) => t.request(
|
|
@@ -793,7 +821,7 @@ var experimentsResource = {
|
|
|
793
821
|
"",
|
|
794
822
|
"**Identity.** Stable `name` (a-z, 0-9, `_`/`-`, max 64 chars). Immutable after create.",
|
|
795
823
|
"",
|
|
796
|
-
"**Lifecycle.** `draft \u2192 running \u2192 stopped \u2192 archived`. Transition via `POST /{id}/status`.
|
|
824
|
+
"**Lifecycle.** `draft \u2192 running \u2192 stopped \u2192 archived`. Transition via `POST /{id}/status`. An archived experiment that never started can be restored with `archived \u2192 draft`; restarting an archived experiment directly is not allowed.",
|
|
797
825
|
"",
|
|
798
826
|
"**Allocation.** `allocation_pct` (basis points, 0\u201310000) is the share of the targeted audience enrolled; `groups[].weight` (must sum to 10000) splits the enrolled audience. `targeting_gate` narrows the eligible audience before allocation.",
|
|
799
827
|
"",
|
|
@@ -812,6 +840,7 @@ var experimentsResource = {
|
|
|
812
840
|
{ name: "start", description: "Transition draft experiment to running" },
|
|
813
841
|
{ name: "stop", description: "Stop a running experiment" },
|
|
814
842
|
{ name: "archive", description: "Archive a stopped experiment" },
|
|
843
|
+
{ name: "restore", description: "Restore an archived (never-started) experiment to draft" },
|
|
815
844
|
{ name: "reanalyze", description: "Re-run analysis pass for the experiment" }
|
|
816
845
|
],
|
|
817
846
|
endpoints: [
|
|
@@ -992,8 +1021,9 @@ var experimentsResource = {
|
|
|
992
1021
|
"- `running \u2192 stopped` \u2014 halts allocation. Existing exposures stay in the dataset.",
|
|
993
1022
|
"- `stopped \u2192 archived` \u2014 soft-delete.",
|
|
994
1023
|
"- `draft \u2192 archived` \u2014 discard an unstarted experiment.",
|
|
1024
|
+
"- `archived \u2192 draft` \u2014 restore a soft-deleted experiment so it can be re-completed and started. Allowed only if it never started; one that already ran must be cloned instead.",
|
|
995
1025
|
"",
|
|
996
|
-
"Restarting an `archived` experiment is not allowed
|
|
1026
|
+
"Restarting an `archived` experiment directly is not allowed \u2014 restore it to `draft` first. Returns `409` on illegal transitions and `429` if the plan's `experiments_running` limit is exceeded on `\u2192 running`."
|
|
997
1027
|
].join("\n"),
|
|
998
1028
|
pathParams: { id: "Stable opaque experiment id." },
|
|
999
1029
|
request: experimentStatusUpdateSchema,
|
|
@@ -1014,6 +1044,11 @@ var experimentsResource = {
|
|
|
1014
1044
|
summary: "Archive \u2014 stopped \u2192 archived",
|
|
1015
1045
|
description: "Soft-deletes the experiment. List endpoints exclude archived rows by default.",
|
|
1016
1046
|
value: { status: "archived" }
|
|
1047
|
+
},
|
|
1048
|
+
restore: {
|
|
1049
|
+
summary: "Restore \u2014 archived \u2192 draft",
|
|
1050
|
+
description: "Un-deletes a soft-deleted experiment so it can be re-completed and started. Allowed only if it never ran.",
|
|
1051
|
+
value: { status: "draft" }
|
|
1017
1052
|
}
|
|
1018
1053
|
},
|
|
1019
1054
|
response: { id: "exp_01j7wb12c3d4e5f6g7h8j9k0l1", status: "running" }
|
|
@@ -16,11 +16,7 @@ declare const paramSchemaSchema: z.ZodRecord<z.ZodString, z.ZodEnum<{
|
|
|
16
16
|
number: "number";
|
|
17
17
|
bool: "bool";
|
|
18
18
|
}>>;
|
|
19
|
-
declare const bucketBySchema: z.
|
|
20
|
-
user_id: "user_id";
|
|
21
|
-
session_id: "session_id";
|
|
22
|
-
device_id: "device_id";
|
|
23
|
-
}>;
|
|
19
|
+
declare const bucketBySchema: z.ZodString;
|
|
24
20
|
declare const experimentCreateSchema: z.ZodObject<{
|
|
25
21
|
name: z.ZodString;
|
|
26
22
|
description: z.ZodDefault<z.ZodNullable<z.ZodString>>;
|
|
@@ -28,11 +24,7 @@ declare const experimentCreateSchema: z.ZodObject<{
|
|
|
28
24
|
tag: z.ZodDefault<z.ZodNullable<z.ZodString>>;
|
|
29
25
|
owner_email: z.ZodDefault<z.ZodNullable<z.ZodString>>;
|
|
30
26
|
audience: z.ZodDefault<z.ZodNullable<z.ZodString>>;
|
|
31
|
-
bucket_by: z.ZodDefault<z.ZodNullable<z.
|
|
32
|
-
user_id: "user_id";
|
|
33
|
-
session_id: "session_id";
|
|
34
|
-
device_id: "device_id";
|
|
35
|
-
}>>>;
|
|
27
|
+
bucket_by: z.ZodDefault<z.ZodNullable<z.ZodString>>;
|
|
36
28
|
folder: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
37
29
|
universe: z.ZodString;
|
|
38
30
|
targeting_gate: z.ZodDefault<z.ZodNullable<z.ZodString>>;
|
|
@@ -68,11 +60,7 @@ declare const experimentUpdateSchema: z.ZodObject<{
|
|
|
68
60
|
tag: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
69
61
|
owner_email: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
70
62
|
audience: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
71
|
-
bucket_by: z.ZodOptional<z.ZodNullable<z.
|
|
72
|
-
user_id: "user_id";
|
|
73
|
-
session_id: "session_id";
|
|
74
|
-
device_id: "device_id";
|
|
75
|
-
}>>>;
|
|
63
|
+
bucket_by: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
76
64
|
folder: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
77
65
|
targeting_gate: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
78
66
|
allocation_pct: z.ZodOptional<z.ZodNumber>;
|
|
@@ -127,11 +115,7 @@ declare const experimentResponseSchema: z.ZodObject<{
|
|
|
127
115
|
tag: z.ZodNullable<z.ZodString>;
|
|
128
116
|
ownerEmail: z.ZodNullable<z.ZodString>;
|
|
129
117
|
audience: z.ZodNullable<z.ZodString>;
|
|
130
|
-
bucketBy: z.ZodNullable<z.
|
|
131
|
-
user_id: "user_id";
|
|
132
|
-
session_id: "session_id";
|
|
133
|
-
device_id: "device_id";
|
|
134
|
-
}>>;
|
|
118
|
+
bucketBy: z.ZodNullable<z.ZodString>;
|
|
135
119
|
folder: z.ZodNullable<z.ZodString>;
|
|
136
120
|
status: z.ZodEnum<{
|
|
137
121
|
draft: "draft";
|
|
@@ -31,7 +31,12 @@ var experimentGroupSchema = z3.object({
|
|
|
31
31
|
var paramSchemaSchema = z3.record(z3.string(), z3.enum(["string", "bool", "number"])).describe(
|
|
32
32
|
"Map of param-name \u2192 scalar type. Defines the shape of `groups[].params`. Example: `{ headline: 'string', show_cta: 'bool' }`."
|
|
33
33
|
);
|
|
34
|
-
var bucketBySchema = z3.
|
|
34
|
+
var bucketBySchema = z3.string().min(1).max(128).regex(
|
|
35
|
+
/^[a-zA-Z_][a-zA-Z0-9_]*$/,
|
|
36
|
+
"Must be a user-attribute name (letters, digits, underscore; not starting with a digit)."
|
|
37
|
+
).describe(
|
|
38
|
+
"User-attribute name used as the bucketing key \u2014 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."
|
|
39
|
+
);
|
|
35
40
|
var experimentCreateSchema = z3.object({
|
|
36
41
|
name: experimentNameSchema,
|
|
37
42
|
description: z3.string().max(2e3).nullable().default(null).describe("Free-form description. Max 2000 chars, markdown rendered in the dashboard."),
|
|
@@ -103,7 +108,7 @@ var experimentUpdateSchema = z3.object({
|
|
|
103
108
|
);
|
|
104
109
|
var experimentStatusUpdateSchema = z3.object({
|
|
105
110
|
status: z3.enum(["draft", "running", "stopped", "archived"]).describe(
|
|
106
|
-
"Target status. Allowed transitions: `draft \u2192 running`, `running \u2192 stopped`, `stopped \u2192 archived`, `draft \u2192 archived
|
|
111
|
+
"Target status. Allowed transitions: `draft \u2192 running`, `running \u2192 stopped`, `stopped \u2192 archived`, `draft \u2192 archived`, and `archived \u2192 draft` (restore \u2014 only if the experiment never started). Restarting an archived experiment directly is not allowed; restore it to draft first, then start."
|
|
107
112
|
)
|
|
108
113
|
}).describe("Body for `POST /api/admin/experiments/{id}/status`.");
|
|
109
114
|
var experimentMetricsUpdateSchema = z3.object({
|