@shipeasy/openapi 1.0.0 → 1.2.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.d.ts +9 -7
- package/dist/index.js +504 -2672
- package/dist/resources/alert-rules.d.ts +48 -0
- package/dist/resources/alert-rules.js +66 -0
- package/dist/resources/configs.d.ts +6 -2
- package/dist/resources/configs.js +67 -2375
- package/dist/resources/experiments.d.ts +100 -2
- package/dist/resources/experiments.js +158 -105
- package/dist/resources/gates.d.ts +51 -8
- package/dist/resources/gates.js +102 -70
- package/dist/resources/index.d.ts +166 -10
- package/dist/resources/index.js +503 -2671
- package/dist/resources/killswitches.d.ts +6 -2
- package/dist/resources/killswitches.js +113 -2412
- package/dist/resources/universes.d.ts +7 -4
- package/dist/resources/universes.js +30 -20
- package/dist/schemas/alert-rules.d.ts +43 -0
- package/dist/schemas/alert-rules.js +27 -0
- package/dist/schemas/configs.d.ts +154 -0
- package/dist/schemas/configs.js +134 -0
- package/dist/schemas/constants.d.ts +6 -0
- package/dist/schemas/constants.js +7 -0
- package/dist/schemas/experiments.d.ts +245 -0
- package/dist/schemas/experiments.js +216 -0
- package/dist/schemas/folder.d.ts +10 -0
- package/dist/schemas/folder.js +8 -0
- package/dist/schemas/gates.d.ts +304 -0
- package/dist/schemas/gates.js +172 -0
- package/dist/schemas/index.d.ts +11 -0
- package/dist/schemas/index.js +729 -0
- package/dist/schemas/killswitches.d.ts +110 -0
- package/dist/schemas/killswitches.js +226 -0
- package/dist/schemas/metric-name.d.ts +5 -0
- package/dist/schemas/metric-name.js +6 -0
- package/dist/schemas/pagination.d.ts +37 -0
- package/dist/schemas/pagination.js +44 -0
- package/dist/schemas/universes.d.ts +35 -0
- package/dist/schemas/universes.js +55 -0
- package/openapi.json +1257 -228
- package/package.json +13 -10
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { experimentCreateSchema, experimentUpdateSchema } from '
|
|
3
|
-
import { PageQuery, Page } from '
|
|
2
|
+
import { experimentCreateSchema, experimentUpdateSchema } from '../schemas/experiments.js';
|
|
3
|
+
import { PageQuery, Page } from '../schemas/pagination.js';
|
|
4
4
|
import { Transport } from '../transport.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -59,6 +59,8 @@ interface ExperimentsClient {
|
|
|
59
59
|
start(id: string): Promise<Experiment>;
|
|
60
60
|
stop(id: string): Promise<Experiment>;
|
|
61
61
|
archive(id: string): Promise<Experiment>;
|
|
62
|
+
/** Restore a soft-deleted (archived) experiment back to `draft`. */
|
|
63
|
+
restore(id: string): Promise<Experiment>;
|
|
62
64
|
setMetrics(id: string, input: ExperimentMetricsInput): Promise<unknown>;
|
|
63
65
|
results(id: string): Promise<ExperimentResult[]>;
|
|
64
66
|
timeseries(id: string, metric?: string): Promise<ExperimentTimeseriesPoint[]>;
|
|
@@ -80,7 +82,16 @@ declare const experimentsResource: {
|
|
|
80
82
|
readonly create: z.ZodObject<{
|
|
81
83
|
name: z.ZodString;
|
|
82
84
|
description: z.ZodDefault<z.ZodNullable<z.ZodString>>;
|
|
85
|
+
hypothesis: z.ZodDefault<z.ZodNullable<z.ZodString>>;
|
|
83
86
|
tag: z.ZodDefault<z.ZodNullable<z.ZodString>>;
|
|
87
|
+
owner_email: z.ZodDefault<z.ZodNullable<z.ZodString>>;
|
|
88
|
+
audience: z.ZodDefault<z.ZodNullable<z.ZodString>>;
|
|
89
|
+
bucket_by: z.ZodDefault<z.ZodNullable<z.ZodEnum<{
|
|
90
|
+
user_id: "user_id";
|
|
91
|
+
session_id: "session_id";
|
|
92
|
+
device_id: "device_id";
|
|
93
|
+
}>>>;
|
|
94
|
+
folder: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
84
95
|
universe: z.ZodString;
|
|
85
96
|
targeting_gate: z.ZodDefault<z.ZodNullable<z.ZodString>>;
|
|
86
97
|
allocation_pct: z.ZodDefault<z.ZodNumber>;
|
|
@@ -99,11 +110,28 @@ declare const experimentsResource: {
|
|
|
99
110
|
min_runtime_days: z.ZodDefault<z.ZodNumber>;
|
|
100
111
|
min_sample_size: z.ZodDefault<z.ZodNumber>;
|
|
101
112
|
sequential_testing: z.ZodDefault<z.ZodBoolean>;
|
|
113
|
+
goal_metric: z.ZodOptional<z.ZodObject<{
|
|
114
|
+
name: z.ZodOptional<z.ZodString>;
|
|
115
|
+
query: z.ZodString;
|
|
116
|
+
}, z.core.$strip>>;
|
|
117
|
+
guardrail_metrics: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
118
|
+
name: z.ZodOptional<z.ZodString>;
|
|
119
|
+
query: z.ZodString;
|
|
120
|
+
}, z.core.$strip>>>;
|
|
102
121
|
}, z.core.$strip>;
|
|
103
122
|
readonly update: z.ZodObject<{
|
|
104
123
|
name: z.ZodOptional<z.ZodString>;
|
|
105
124
|
description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
125
|
+
hypothesis: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
106
126
|
tag: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
127
|
+
owner_email: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
128
|
+
audience: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
129
|
+
bucket_by: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
|
|
130
|
+
user_id: "user_id";
|
|
131
|
+
session_id: "session_id";
|
|
132
|
+
device_id: "device_id";
|
|
133
|
+
}>>>;
|
|
134
|
+
folder: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
107
135
|
targeting_gate: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
108
136
|
allocation_pct: z.ZodOptional<z.ZodNumber>;
|
|
109
137
|
salt: z.ZodOptional<z.ZodString>;
|
|
@@ -122,6 +150,14 @@ declare const experimentsResource: {
|
|
|
122
150
|
min_runtime_days: z.ZodOptional<z.ZodNumber>;
|
|
123
151
|
min_sample_size: z.ZodOptional<z.ZodNumber>;
|
|
124
152
|
sequential_testing: z.ZodOptional<z.ZodBoolean>;
|
|
153
|
+
goal_metric: z.ZodOptional<z.ZodObject<{
|
|
154
|
+
name: z.ZodOptional<z.ZodString>;
|
|
155
|
+
query: z.ZodString;
|
|
156
|
+
}, z.core.$strip>>;
|
|
157
|
+
guardrail_metrics: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
158
|
+
name: z.ZodOptional<z.ZodString>;
|
|
159
|
+
query: z.ZodString;
|
|
160
|
+
}, z.core.$strip>>>;
|
|
125
161
|
}, z.core.$strip>;
|
|
126
162
|
};
|
|
127
163
|
readonly actions: readonly [{
|
|
@@ -133,6 +169,9 @@ declare const experimentsResource: {
|
|
|
133
169
|
}, {
|
|
134
170
|
readonly name: "archive";
|
|
135
171
|
readonly description: "Archive a stopped experiment";
|
|
172
|
+
}, {
|
|
173
|
+
readonly name: "restore";
|
|
174
|
+
readonly description: "Restore an archived (never-started) experiment to draft";
|
|
136
175
|
}, {
|
|
137
176
|
readonly name: "reanalyze";
|
|
138
177
|
readonly description: "Re-run analysis pass for the experiment";
|
|
@@ -148,7 +187,16 @@ declare const experimentsResource: {
|
|
|
148
187
|
id: z.ZodString;
|
|
149
188
|
name: z.ZodString;
|
|
150
189
|
description: z.ZodNullable<z.ZodString>;
|
|
190
|
+
hypothesis: z.ZodNullable<z.ZodString>;
|
|
151
191
|
tag: z.ZodNullable<z.ZodString>;
|
|
192
|
+
ownerEmail: z.ZodNullable<z.ZodString>;
|
|
193
|
+
audience: z.ZodNullable<z.ZodString>;
|
|
194
|
+
bucketBy: z.ZodNullable<z.ZodEnum<{
|
|
195
|
+
user_id: "user_id";
|
|
196
|
+
session_id: "session_id";
|
|
197
|
+
device_id: "device_id";
|
|
198
|
+
}>>;
|
|
199
|
+
folder: z.ZodNullable<z.ZodString>;
|
|
152
200
|
status: z.ZodEnum<{
|
|
153
201
|
draft: "draft";
|
|
154
202
|
running: "running";
|
|
@@ -226,7 +274,16 @@ declare const experimentsResource: {
|
|
|
226
274
|
id: z.ZodString;
|
|
227
275
|
name: z.ZodString;
|
|
228
276
|
description: z.ZodNullable<z.ZodString>;
|
|
277
|
+
hypothesis: z.ZodNullable<z.ZodString>;
|
|
229
278
|
tag: z.ZodNullable<z.ZodString>;
|
|
279
|
+
ownerEmail: z.ZodNullable<z.ZodString>;
|
|
280
|
+
audience: z.ZodNullable<z.ZodString>;
|
|
281
|
+
bucketBy: z.ZodNullable<z.ZodEnum<{
|
|
282
|
+
user_id: "user_id";
|
|
283
|
+
session_id: "session_id";
|
|
284
|
+
device_id: "device_id";
|
|
285
|
+
}>>;
|
|
286
|
+
folder: z.ZodNullable<z.ZodString>;
|
|
230
287
|
status: z.ZodEnum<{
|
|
231
288
|
draft: "draft";
|
|
232
289
|
running: "running";
|
|
@@ -296,7 +353,16 @@ declare const experimentsResource: {
|
|
|
296
353
|
readonly request: z.ZodObject<{
|
|
297
354
|
name: z.ZodString;
|
|
298
355
|
description: z.ZodDefault<z.ZodNullable<z.ZodString>>;
|
|
356
|
+
hypothesis: z.ZodDefault<z.ZodNullable<z.ZodString>>;
|
|
299
357
|
tag: z.ZodDefault<z.ZodNullable<z.ZodString>>;
|
|
358
|
+
owner_email: z.ZodDefault<z.ZodNullable<z.ZodString>>;
|
|
359
|
+
audience: z.ZodDefault<z.ZodNullable<z.ZodString>>;
|
|
360
|
+
bucket_by: z.ZodDefault<z.ZodNullable<z.ZodEnum<{
|
|
361
|
+
user_id: "user_id";
|
|
362
|
+
session_id: "session_id";
|
|
363
|
+
device_id: "device_id";
|
|
364
|
+
}>>>;
|
|
365
|
+
folder: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
300
366
|
universe: z.ZodString;
|
|
301
367
|
targeting_gate: z.ZodDefault<z.ZodNullable<z.ZodString>>;
|
|
302
368
|
allocation_pct: z.ZodDefault<z.ZodNumber>;
|
|
@@ -315,6 +381,14 @@ declare const experimentsResource: {
|
|
|
315
381
|
min_runtime_days: z.ZodDefault<z.ZodNumber>;
|
|
316
382
|
min_sample_size: z.ZodDefault<z.ZodNumber>;
|
|
317
383
|
sequential_testing: z.ZodDefault<z.ZodBoolean>;
|
|
384
|
+
goal_metric: z.ZodOptional<z.ZodObject<{
|
|
385
|
+
name: z.ZodOptional<z.ZodString>;
|
|
386
|
+
query: z.ZodString;
|
|
387
|
+
}, z.core.$strip>>;
|
|
388
|
+
guardrail_metrics: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
389
|
+
name: z.ZodOptional<z.ZodString>;
|
|
390
|
+
query: z.ZodString;
|
|
391
|
+
}, z.core.$strip>>>;
|
|
318
392
|
}, z.core.$strip>;
|
|
319
393
|
readonly response: z.ZodObject<{
|
|
320
394
|
id: z.ZodString;
|
|
@@ -418,7 +492,16 @@ declare const experimentsResource: {
|
|
|
418
492
|
readonly request: z.ZodObject<{
|
|
419
493
|
name: z.ZodOptional<z.ZodString>;
|
|
420
494
|
description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
495
|
+
hypothesis: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
421
496
|
tag: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
497
|
+
owner_email: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
498
|
+
audience: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
499
|
+
bucket_by: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
|
|
500
|
+
user_id: "user_id";
|
|
501
|
+
session_id: "session_id";
|
|
502
|
+
device_id: "device_id";
|
|
503
|
+
}>>>;
|
|
504
|
+
folder: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
422
505
|
targeting_gate: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
423
506
|
allocation_pct: z.ZodOptional<z.ZodNumber>;
|
|
424
507
|
salt: z.ZodOptional<z.ZodString>;
|
|
@@ -437,6 +520,14 @@ declare const experimentsResource: {
|
|
|
437
520
|
min_runtime_days: z.ZodOptional<z.ZodNumber>;
|
|
438
521
|
min_sample_size: z.ZodOptional<z.ZodNumber>;
|
|
439
522
|
sequential_testing: z.ZodOptional<z.ZodBoolean>;
|
|
523
|
+
goal_metric: z.ZodOptional<z.ZodObject<{
|
|
524
|
+
name: z.ZodOptional<z.ZodString>;
|
|
525
|
+
query: z.ZodString;
|
|
526
|
+
}, z.core.$strip>>;
|
|
527
|
+
guardrail_metrics: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
528
|
+
name: z.ZodOptional<z.ZodString>;
|
|
529
|
+
query: z.ZodString;
|
|
530
|
+
}, z.core.$strip>>>;
|
|
440
531
|
}, z.core.$strip>;
|
|
441
532
|
readonly response: z.ZodObject<{
|
|
442
533
|
id: z.ZodString;
|
|
@@ -551,6 +642,13 @@ declare const experimentsResource: {
|
|
|
551
642
|
readonly status: "archived";
|
|
552
643
|
};
|
|
553
644
|
};
|
|
645
|
+
readonly restore: {
|
|
646
|
+
readonly summary: "Restore — archived → draft";
|
|
647
|
+
readonly description: "Un-deletes a soft-deleted experiment so it can be re-completed and started. Allowed only if it never ran.";
|
|
648
|
+
readonly value: {
|
|
649
|
+
readonly status: "draft";
|
|
650
|
+
};
|
|
651
|
+
};
|
|
554
652
|
};
|
|
555
653
|
readonly response: {
|
|
556
654
|
readonly id: "exp_01j7wb12c3d4e5f6g7h8j9k0l1";
|
|
@@ -1,48 +1,76 @@
|
|
|
1
1
|
// src/resources/experiments.ts
|
|
2
|
-
import { z as
|
|
2
|
+
import { z as z4 } from "zod";
|
|
3
|
+
|
|
4
|
+
// src/schemas/experiments.ts
|
|
5
|
+
import { z as z3 } from "zod";
|
|
3
6
|
|
|
4
|
-
//
|
|
7
|
+
// src/schemas/folder.ts
|
|
5
8
|
import { z } from "zod";
|
|
6
|
-
var
|
|
7
|
-
"
|
|
9
|
+
var folderSchema = z.string().max(256).regex(/^[a-zA-Z0-9_-]+$/, "Folder must be alphanumeric, `_` or `-` (no `/`).").nullable().optional().describe(
|
|
10
|
+
"Optional folder name used to group items in the dashboard. Part of the SDK lookup key: an item in folder `checkout` named `new-cart` is referenced as `checkout/new-cart` from the SDK."
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
// src/schemas/metric-name.ts
|
|
14
|
+
import { z as z2 } from "zod";
|
|
15
|
+
var metricNameSchema = z2.string().regex(/^[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?)?$/).max(128);
|
|
16
|
+
|
|
17
|
+
// src/schemas/experiments.ts
|
|
18
|
+
var experimentInlineMetricSchema = z3.object({
|
|
19
|
+
name: metricNameSchema.optional(),
|
|
20
|
+
query: z3.string().min(1).max(4096).describe("Metric DSL string, e.g. `count_users(checkout_completed)`.")
|
|
21
|
+
});
|
|
22
|
+
var experimentNameSchema = z3.string().regex(/^[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?)?$/).max(128).describe(
|
|
23
|
+
"Stable experiment key. Single segment or `folder.name` (a-z, 0-9, `_`/`-`; max 128 chars). Used by SDKs as `Shipeasy.getExperiment(user, '<name>')`. Immutable after create."
|
|
8
24
|
);
|
|
9
|
-
var experimentGroupSchema =
|
|
10
|
-
name:
|
|
11
|
-
weight:
|
|
25
|
+
var experimentGroupSchema = z3.object({
|
|
26
|
+
name: z3.string().min(1).max(64).describe("Group label (e.g. `control`, `treatment_a`). Max 64 chars."),
|
|
27
|
+
weight: z3.number().int().min(0).max(1e4).describe(
|
|
12
28
|
"Allocation weight in basis points (0\u201310000). The sum across all groups must equal exactly **10000** (100%)."
|
|
13
29
|
),
|
|
14
|
-
params:
|
|
30
|
+
params: z3.record(z3.string(), z3.unknown()).default({}).describe(
|
|
15
31
|
"Per-group parameter values delivered to the SDK when a caller is hashed into this group. Keys must match the experiment's `params` schema."
|
|
16
32
|
)
|
|
17
33
|
}).describe("One variant in the experiment. Weights across all groups must sum to 10000.");
|
|
18
|
-
var paramSchemaSchema =
|
|
34
|
+
var paramSchemaSchema = z3.record(z3.string(), z3.enum(["string", "bool", "number"])).describe(
|
|
19
35
|
"Map of param-name \u2192 scalar type. Defines the shape of `groups[].params`. Example: `{ headline: 'string', show_cta: 'bool' }`."
|
|
20
36
|
);
|
|
21
|
-
var
|
|
37
|
+
var bucketBySchema = z3.enum(["user_id", "session_id", "device_id"]).describe("Identifier used as the bucketing key. `user_id` is sticky across sessions.");
|
|
38
|
+
var experimentCreateSchema = z3.object({
|
|
22
39
|
name: experimentNameSchema,
|
|
23
|
-
description:
|
|
24
|
-
|
|
25
|
-
|
|
40
|
+
description: z3.string().max(2e3).nullable().default(null).describe("Free-form description. Max 2000 chars, markdown rendered in the dashboard."),
|
|
41
|
+
hypothesis: z3.string().max(4e3).nullable().default(null).describe("Hypothesis statement shown in the editor. Display-only."),
|
|
42
|
+
tag: z3.string().max(64).nullable().default(null).describe("Short tag chip rendered next to the name. Display-only."),
|
|
43
|
+
owner_email: z3.string().max(254).nullable().default(null).describe("Owner email. Display-only."),
|
|
44
|
+
audience: z3.string().max(256).nullable().default(null).describe("Audience label shown in the editor. Display-only."),
|
|
45
|
+
bucket_by: bucketBySchema.nullable().default(null),
|
|
46
|
+
folder: folderSchema,
|
|
47
|
+
universe: z3.string().min(1).describe(
|
|
26
48
|
"Name of an existing universe in the project. Returns `422` if the universe doesn't exist."
|
|
27
49
|
),
|
|
28
|
-
targeting_gate:
|
|
50
|
+
targeting_gate: z3.string().nullable().default(null).describe(
|
|
29
51
|
"Optional gate name. Only callers that pass the gate are enrolled in the experiment."
|
|
30
52
|
),
|
|
31
|
-
allocation_pct:
|
|
53
|
+
allocation_pct: z3.number().int().min(0).max(1e4).default(0).describe(
|
|
32
54
|
"Share of the (gated) audience allocated to the experiment, in basis points (0\u201310000 = 0%\u2013100%). `0` = unallocated. Immutable while the experiment is running."
|
|
33
55
|
),
|
|
34
|
-
salt:
|
|
56
|
+
salt: z3.string().min(1).max(64).optional().describe("Hash salt for bucketing. Auto-generated if omitted. Immutable while running."),
|
|
35
57
|
params: paramSchemaSchema.default({}),
|
|
36
|
-
groups:
|
|
58
|
+
groups: z3.array(experimentGroupSchema).min(2).describe(
|
|
37
59
|
"Two or more variants. Weights must sum to exactly 10000 (100%). Immutable while running."
|
|
38
60
|
),
|
|
39
|
-
significance_threshold:
|
|
61
|
+
significance_threshold: z3.number().min(1e-4).max(0.5).default(0.05).describe(
|
|
40
62
|
"p-value cutoff used by the analysis pass. Defaults to `0.05`. Values other than 0.05 require Pro plan or higher."
|
|
41
63
|
),
|
|
42
|
-
min_runtime_days:
|
|
43
|
-
min_sample_size:
|
|
44
|
-
sequential_testing:
|
|
64
|
+
min_runtime_days: z3.number().int().min(0).max(365).default(0).describe("Minimum days the experiment must run before results are considered conclusive."),
|
|
65
|
+
min_sample_size: z3.number().int().min(1).default(100).describe("Minimum exposures per group before results are considered conclusive."),
|
|
66
|
+
sequential_testing: z3.boolean().default(false).describe(
|
|
45
67
|
"Enable sequential testing (always-valid p-values). Requires Premium plan or higher."
|
|
68
|
+
),
|
|
69
|
+
goal_metric: experimentInlineMetricSchema.optional().describe(
|
|
70
|
+
"Single goal metric defined inline as a DSL query. The underlying event is auto-created if missing."
|
|
71
|
+
),
|
|
72
|
+
guardrail_metrics: z3.array(experimentInlineMetricSchema).max(10).default([]).describe(
|
|
73
|
+
"Up to 10 guardrail metrics defined inline. Each is upserted (event + metric) and attached with role=guardrail."
|
|
46
74
|
)
|
|
47
75
|
}).refine(
|
|
48
76
|
(e) => e.groups.reduce((s, g) => s + g.weight, 0) === 1e4,
|
|
@@ -50,35 +78,42 @@ var experimentCreateSchema = z.object({
|
|
|
50
78
|
).describe(
|
|
51
79
|
"Body for `POST /api/admin/experiments`. `name`, `universe`, and `groups` (\u22652, weights sum to 10000) required."
|
|
52
80
|
);
|
|
53
|
-
var experimentUpdateSchema =
|
|
81
|
+
var experimentUpdateSchema = z3.object({
|
|
54
82
|
name: experimentNameSchema.optional(),
|
|
55
|
-
description:
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
83
|
+
description: z3.string().max(2e3).nullable().optional(),
|
|
84
|
+
hypothesis: z3.string().max(4e3).nullable().optional(),
|
|
85
|
+
tag: z3.string().max(64).nullable().optional(),
|
|
86
|
+
owner_email: z3.string().max(254).nullable().optional(),
|
|
87
|
+
audience: z3.string().max(256).nullable().optional(),
|
|
88
|
+
bucket_by: bucketBySchema.nullable().optional(),
|
|
89
|
+
folder: folderSchema,
|
|
90
|
+
targeting_gate: z3.string().nullable().optional(),
|
|
91
|
+
allocation_pct: z3.number().int().min(0).max(1e4).optional().describe("Basis-points allocation (0\u201310000). Immutable while the experiment is running."),
|
|
92
|
+
salt: z3.string().min(1).max(64).optional().describe("Hash salt. Immutable while running."),
|
|
93
|
+
universe: z3.string().optional().describe(
|
|
61
94
|
"New universe name. Immutable while running. Returns `422` if the universe doesn't exist."
|
|
62
95
|
),
|
|
63
96
|
params: paramSchemaSchema.optional(),
|
|
64
|
-
groups:
|
|
65
|
-
significance_threshold:
|
|
66
|
-
min_runtime_days:
|
|
67
|
-
min_sample_size:
|
|
68
|
-
sequential_testing:
|
|
97
|
+
groups: z3.array(experimentGroupSchema).min(2).optional().describe("Replacement groups. Weights must sum to 10000. Immutable while running."),
|
|
98
|
+
significance_threshold: z3.number().min(1e-4).max(0.5).optional(),
|
|
99
|
+
min_runtime_days: z3.number().int().min(0).max(365).optional(),
|
|
100
|
+
min_sample_size: z3.number().int().min(1).optional(),
|
|
101
|
+
sequential_testing: z3.boolean().optional(),
|
|
102
|
+
goal_metric: experimentInlineMetricSchema.optional().describe("Replaces the goal metric (event auto-upserted)."),
|
|
103
|
+
guardrail_metrics: z3.array(experimentInlineMetricSchema).max(10).optional().describe("Replaces the guardrail set wholesale (event auto-upserted per entry).")
|
|
69
104
|
}).describe(
|
|
70
105
|
"Body for `PATCH /api/admin/experiments/{id}`. Partial \u2014 only supplied fields change. `allocation_pct`, `groups`, `salt`, `universe`, `params` are immutable while the experiment is running (stop first)."
|
|
71
106
|
);
|
|
72
|
-
var experimentStatusUpdateSchema =
|
|
73
|
-
status:
|
|
74
|
-
"Target status. Allowed transitions: `draft \u2192 running`, `running \u2192 stopped`, `stopped \u2192 archived`, `draft \u2192 archived
|
|
107
|
+
var experimentStatusUpdateSchema = z3.object({
|
|
108
|
+
status: z3.enum(["draft", "running", "stopped", "archived"]).describe(
|
|
109
|
+
"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."
|
|
75
110
|
)
|
|
76
111
|
}).describe("Body for `POST /api/admin/experiments/{id}/status`.");
|
|
77
|
-
var experimentMetricsUpdateSchema =
|
|
78
|
-
metrics:
|
|
79
|
-
|
|
80
|
-
metric_id:
|
|
81
|
-
role:
|
|
112
|
+
var experimentMetricsUpdateSchema = z3.object({
|
|
113
|
+
metrics: z3.array(
|
|
114
|
+
z3.object({
|
|
115
|
+
metric_id: z3.string().describe("Existing metric id in the project."),
|
|
116
|
+
role: z3.enum(["goal", "guardrail", "secondary"]).describe(
|
|
82
117
|
"Metric role. `goal` drives the decision, `guardrail` blocks ship if degraded, `secondary` is informational."
|
|
83
118
|
)
|
|
84
119
|
})
|
|
@@ -86,76 +121,81 @@ var experimentMetricsUpdateSchema = z.object({
|
|
|
86
121
|
}).describe(
|
|
87
122
|
"Body for `POST /api/admin/experiments/{id}/metrics`. Replaces the experiment's metric attachments wholesale. Returns `422` if any `metric_id` is unknown."
|
|
88
123
|
);
|
|
89
|
-
var experimentResponseSchema =
|
|
90
|
-
id:
|
|
124
|
+
var experimentResponseSchema = z3.object({
|
|
125
|
+
id: z3.string().describe("Stable opaque experiment id (`exp_\u2026`)."),
|
|
91
126
|
name: experimentNameSchema,
|
|
92
|
-
description:
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
127
|
+
description: z3.string().nullable(),
|
|
128
|
+
hypothesis: z3.string().nullable(),
|
|
129
|
+
tag: z3.string().nullable(),
|
|
130
|
+
ownerEmail: z3.string().nullable(),
|
|
131
|
+
audience: z3.string().nullable(),
|
|
132
|
+
bucketBy: bucketBySchema.nullable(),
|
|
133
|
+
folder: z3.string().nullable(),
|
|
134
|
+
status: z3.enum(["draft", "running", "stopped", "archived"]),
|
|
135
|
+
universe: z3.string().describe("Universe name this experiment draws from."),
|
|
136
|
+
targetingGate: z3.string().nullable(),
|
|
137
|
+
allocationPct: z3.number().int().describe("Allocation in basis points (0\u201310000)."),
|
|
138
|
+
salt: z3.string(),
|
|
139
|
+
params: z3.record(z3.string(), z3.enum(["string", "bool", "number"])),
|
|
140
|
+
groups: z3.array(experimentGroupSchema),
|
|
141
|
+
significanceThreshold: z3.number(),
|
|
142
|
+
minRuntimeDays: z3.number().int(),
|
|
143
|
+
minSampleSize: z3.number().int(),
|
|
144
|
+
sequentialTesting: z3.boolean(),
|
|
145
|
+
startedAt: z3.string().nullable().describe("ISO-8601 timestamp the experiment last transitioned to `running`, or `null`."),
|
|
146
|
+
stoppedAt: z3.string().nullable().optional(),
|
|
147
|
+
updatedAt: z3.string()
|
|
108
148
|
});
|
|
109
|
-
var experimentCreateResponseSchema =
|
|
110
|
-
id:
|
|
149
|
+
var experimentCreateResponseSchema = z3.object({
|
|
150
|
+
id: z3.string().describe("Newly assigned experiment id."),
|
|
111
151
|
name: experimentNameSchema
|
|
112
152
|
});
|
|
113
|
-
var experimentUpdateResponseSchema =
|
|
114
|
-
id:
|
|
153
|
+
var experimentUpdateResponseSchema = z3.object({
|
|
154
|
+
id: z3.string().describe("Experiment id that was updated.")
|
|
115
155
|
});
|
|
116
|
-
var experimentDeleteResponseSchema =
|
|
117
|
-
var experimentStatusResponseSchema =
|
|
118
|
-
id:
|
|
119
|
-
status:
|
|
156
|
+
var experimentDeleteResponseSchema = z3.object({ ok: z3.literal(true) });
|
|
157
|
+
var experimentStatusResponseSchema = z3.object({
|
|
158
|
+
id: z3.string(),
|
|
159
|
+
status: z3.enum(["draft", "running", "stopped", "archived"])
|
|
120
160
|
});
|
|
121
|
-
var experimentMetricsResponseSchema =
|
|
122
|
-
id:
|
|
123
|
-
metrics:
|
|
124
|
-
|
|
125
|
-
metric_id:
|
|
126
|
-
role:
|
|
161
|
+
var experimentMetricsResponseSchema = z3.object({
|
|
162
|
+
id: z3.string(),
|
|
163
|
+
metrics: z3.array(
|
|
164
|
+
z3.object({
|
|
165
|
+
metric_id: z3.string(),
|
|
166
|
+
role: z3.enum(["goal", "guardrail", "secondary"])
|
|
127
167
|
})
|
|
128
168
|
)
|
|
129
169
|
});
|
|
130
|
-
var experimentResultRowSchema =
|
|
131
|
-
metric:
|
|
132
|
-
group_name:
|
|
133
|
-
ds:
|
|
134
|
-
n:
|
|
135
|
-
mean:
|
|
136
|
-
delta_pct:
|
|
137
|
-
p_value:
|
|
138
|
-
srm_detected:
|
|
170
|
+
var experimentResultRowSchema = z3.object({
|
|
171
|
+
metric: z3.string(),
|
|
172
|
+
group_name: z3.string(),
|
|
173
|
+
ds: z3.string().describe("Date slice (`YYYY-MM-DD`)."),
|
|
174
|
+
n: z3.number().nullable(),
|
|
175
|
+
mean: z3.number().nullable(),
|
|
176
|
+
delta_pct: z3.number().nullable(),
|
|
177
|
+
p_value: z3.number().nullable(),
|
|
178
|
+
srm_detected: z3.number().nullable().describe("`1` if sample-ratio mismatch detected, else `0`.")
|
|
139
179
|
});
|
|
140
|
-
var experimentResultsResponseSchema =
|
|
141
|
-
experiment:
|
|
142
|
-
id:
|
|
180
|
+
var experimentResultsResponseSchema = z3.object({
|
|
181
|
+
experiment: z3.object({
|
|
182
|
+
id: z3.string(),
|
|
143
183
|
name: experimentNameSchema,
|
|
144
|
-
status:
|
|
184
|
+
status: z3.enum(["draft", "running", "stopped", "archived"])
|
|
145
185
|
}),
|
|
146
|
-
results:
|
|
186
|
+
results: z3.array(experimentResultRowSchema)
|
|
147
187
|
});
|
|
148
|
-
var experimentTimeseriesResponseSchema =
|
|
149
|
-
experiment:
|
|
150
|
-
id:
|
|
188
|
+
var experimentTimeseriesResponseSchema = z3.object({
|
|
189
|
+
experiment: z3.object({
|
|
190
|
+
id: z3.string(),
|
|
151
191
|
name: experimentNameSchema,
|
|
152
|
-
status:
|
|
192
|
+
status: z3.enum(["draft", "running", "stopped", "archived"])
|
|
153
193
|
}),
|
|
154
|
-
series:
|
|
194
|
+
series: z3.array(experimentResultRowSchema)
|
|
155
195
|
});
|
|
156
|
-
var experimentReanalyzeResponseSchema =
|
|
157
|
-
id:
|
|
158
|
-
queued:
|
|
196
|
+
var experimentReanalyzeResponseSchema = z3.object({
|
|
197
|
+
id: z3.string(),
|
|
198
|
+
queued: z3.literal(true)
|
|
159
199
|
});
|
|
160
200
|
|
|
161
201
|
// src/transport.ts
|
|
@@ -169,9 +209,9 @@ var ApiError = class extends Error {
|
|
|
169
209
|
};
|
|
170
210
|
|
|
171
211
|
// src/resources/experiments.ts
|
|
172
|
-
var experimentListResponseSchema =
|
|
173
|
-
data:
|
|
174
|
-
next_cursor:
|
|
212
|
+
var experimentListResponseSchema = z4.object({
|
|
213
|
+
data: z4.array(experimentResponseSchema),
|
|
214
|
+
next_cursor: z4.string().nullable()
|
|
175
215
|
});
|
|
176
216
|
var BASE = "/api/admin/experiments";
|
|
177
217
|
function experimentsClient(t) {
|
|
@@ -181,24 +221,29 @@ function experimentsClient(t) {
|
|
|
181
221
|
if (opts.cursor) query.cursor = opts.cursor;
|
|
182
222
|
return t.request("GET", BASE, void 0, query);
|
|
183
223
|
}
|
|
184
|
-
async function
|
|
224
|
+
async function listAllWith(status) {
|
|
185
225
|
const out = [];
|
|
186
226
|
let cursor;
|
|
187
227
|
do {
|
|
188
|
-
const
|
|
228
|
+
const query = { limit: "500" };
|
|
229
|
+
if (cursor) query.cursor = cursor;
|
|
230
|
+
if (status) query.status = status;
|
|
231
|
+
const page = await t.request("GET", BASE, void 0, query);
|
|
189
232
|
out.push(...page.data);
|
|
190
233
|
cursor = page.next_cursor ?? void 0;
|
|
191
234
|
} while (cursor);
|
|
192
235
|
return out;
|
|
193
236
|
}
|
|
237
|
+
async function listAll() {
|
|
238
|
+
return listAllWith();
|
|
239
|
+
}
|
|
194
240
|
async function resolve(idOrName) {
|
|
195
241
|
try {
|
|
196
242
|
return await t.request("GET", `${BASE}/${idOrName}`);
|
|
197
243
|
} catch (err) {
|
|
198
244
|
if (!(err instanceof ApiError) || err.status !== 404) throw err;
|
|
199
245
|
}
|
|
200
|
-
const
|
|
201
|
-
const found = all.find((e) => e.name === idOrName);
|
|
246
|
+
const found = (await listAllWith()).find((e) => e.name === idOrName) ?? (await listAllWith("archived")).find((e) => e.name === idOrName);
|
|
202
247
|
if (!found) throw new ApiError(`Experiment '${idOrName}' not found`, 404);
|
|
203
248
|
return found;
|
|
204
249
|
}
|
|
@@ -221,6 +266,7 @@ function experimentsClient(t) {
|
|
|
221
266
|
start: (id) => setStatus(id, "running"),
|
|
222
267
|
stop: (id) => setStatus(id, "stopped"),
|
|
223
268
|
archive: (id) => setStatus(id, "archived"),
|
|
269
|
+
restore: (id) => setStatus(id, "draft"),
|
|
224
270
|
setMetrics: (id, input) => t.request("POST", `${BASE}/${id}/metrics`, experimentMetricsUpdateSchema.parse(input)),
|
|
225
271
|
results: (id) => t.request("GET", `${BASE}/${id}/results`),
|
|
226
272
|
timeseries: (id, metric) => t.request(
|
|
@@ -267,7 +313,7 @@ var experimentsResource = {
|
|
|
267
313
|
"",
|
|
268
314
|
"**Identity.** Stable `name` (a-z, 0-9, `_`/`-`, max 64 chars). Immutable after create.",
|
|
269
315
|
"",
|
|
270
|
-
"**Lifecycle.** `draft \u2192 running \u2192 stopped \u2192 archived`. Transition via `POST /{id}/status`.
|
|
316
|
+
"**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.",
|
|
271
317
|
"",
|
|
272
318
|
"**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.",
|
|
273
319
|
"",
|
|
@@ -286,6 +332,7 @@ var experimentsResource = {
|
|
|
286
332
|
{ name: "start", description: "Transition draft experiment to running" },
|
|
287
333
|
{ name: "stop", description: "Stop a running experiment" },
|
|
288
334
|
{ name: "archive", description: "Archive a stopped experiment" },
|
|
335
|
+
{ name: "restore", description: "Restore an archived (never-started) experiment to draft" },
|
|
289
336
|
{ name: "reanalyze", description: "Re-run analysis pass for the experiment" }
|
|
290
337
|
],
|
|
291
338
|
endpoints: [
|
|
@@ -466,8 +513,9 @@ var experimentsResource = {
|
|
|
466
513
|
"- `running \u2192 stopped` \u2014 halts allocation. Existing exposures stay in the dataset.",
|
|
467
514
|
"- `stopped \u2192 archived` \u2014 soft-delete.",
|
|
468
515
|
"- `draft \u2192 archived` \u2014 discard an unstarted experiment.",
|
|
516
|
+
"- `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.",
|
|
469
517
|
"",
|
|
470
|
-
"Restarting an `archived` experiment is not allowed
|
|
518
|
+
"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`."
|
|
471
519
|
].join("\n"),
|
|
472
520
|
pathParams: { id: "Stable opaque experiment id." },
|
|
473
521
|
request: experimentStatusUpdateSchema,
|
|
@@ -488,6 +536,11 @@ var experimentsResource = {
|
|
|
488
536
|
summary: "Archive \u2014 stopped \u2192 archived",
|
|
489
537
|
description: "Soft-deletes the experiment. List endpoints exclude archived rows by default.",
|
|
490
538
|
value: { status: "archived" }
|
|
539
|
+
},
|
|
540
|
+
restore: {
|
|
541
|
+
summary: "Restore \u2014 archived \u2192 draft",
|
|
542
|
+
description: "Un-deletes a soft-deleted experiment so it can be re-completed and started. Allowed only if it never ran.",
|
|
543
|
+
value: { status: "draft" }
|
|
491
544
|
}
|
|
492
545
|
},
|
|
493
546
|
response: { id: "exp_01j7wb12c3d4e5f6g7h8j9k0l1", status: "running" }
|
|
@@ -592,7 +645,7 @@ var experimentsResource = {
|
|
|
592
645
|
pathParams: { id: "Stable opaque experiment id." },
|
|
593
646
|
queryParams: {
|
|
594
647
|
metric: {
|
|
595
|
-
schema:
|
|
648
|
+
schema: z4.string().optional(),
|
|
596
649
|
description: "Optional metric name to filter the series."
|
|
597
650
|
}
|
|
598
651
|
},
|