@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 CHANGED
@@ -108,12 +108,14 @@ var gateRuleSchema = z3.object({
108
108
  "semver_gt",
109
109
  "semver_gte",
110
110
  "semver_lt",
111
- "semver_lte"
111
+ "semver_lte",
112
+ "gate_pass",
113
+ "exp_in"
112
114
  ]).describe(
113
- "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)."
115
+ '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).'
114
116
  ),
115
117
  value: z3.unknown().describe(
116
- "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`."
118
+ '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`.'
117
119
  )
118
120
  }).describe(
119
121
  "Single targeting predicate. A gate's `rules` array is evaluated with AND semantics; use the gatekeeper `stack` for OR / fall-through logic."
@@ -124,9 +126,17 @@ var gateNameSchema = z3.string().regex(
124
126
  ).max(128).describe(
125
127
  "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."
126
128
  );
129
+ var rampSchema = z3.object({
130
+ from: z3.number().int().min(0).max(1e4).describe("Start of the ramp in basis points (0\u201310000)."),
131
+ to: z3.number().int().min(0).max(1e4).describe("End of the ramp in basis points (0\u201310000)."),
132
+ startAt: z3.number().int().nonnegative().describe("Ramp start, epoch milliseconds (UTC)."),
133
+ durationMs: z3.number().int().positive().describe("Ramp length in milliseconds.")
134
+ }).describe(
135
+ "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."
136
+ );
127
137
  var stackedConditionSchema = z3.object({
128
138
  id: z3.string().min(1).describe(
129
- "Client-supplied stable id for the stack entry (used for React keys, audit diffs)."
139
+ "Client-supplied stable id for the stack entry (used for React keys, audit diffs, and as the default per-condition rollout salt)."
130
140
  ),
131
141
  type: z3.literal("condition").describe("Discriminator. `condition` = rule-based predicate sub-gate."),
132
142
  name: z3.string().optional().describe("Human label shown in the dashboard."),
@@ -135,9 +145,15 @@ var stackedConditionSchema = z3.object({
135
145
  ),
136
146
  pass: z3.enum(["all", "any"]).optional().describe("Combinator across `rules`. `all` = AND (default), `any` = OR."),
137
147
  rules: z3.array(gateRuleSchema).default([]).describe("Predicates evaluated under `pass`. Empty array = match nothing."),
148
+ rolloutPct: z3.number().int().min(0).max(1e4).optional().describe(
149
+ "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)."
150
+ ),
151
+ bucketBy: z3.string().optional().describe("Attribute used to hash the caller into the per-condition bucket. Default `userID`."),
152
+ salt: z3.string().optional().describe("Per-condition hash salt. Defaults to the entry `id`, then the gate salt."),
153
+ ramp: rampSchema.optional(),
138
154
  locked: z3.boolean().optional().describe("If `true`, dashboard hides edit controls (e.g. the trailing public-floor entry).")
139
155
  }).describe(
140
- "Rule-based sub-gate. When matched, evaluation short-circuits and the gate returns `true` for the current caller."
156
+ "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."
141
157
  );
142
158
  var stackedRolloutSchema = z3.object({
143
159
  id: z3.string().min(1).describe("Client-supplied stable id for the stack entry."),
@@ -153,9 +169,10 @@ var stackedRolloutSchema = z3.object({
153
169
  salt: z3.string().optional().describe(
154
170
  "Per-entry hash salt. Defaults to the gate's salt. Set explicitly to keep two rollouts independent or correlated across gates."
155
171
  ),
172
+ ramp: rampSchema.optional(),
156
173
  locked: z3.boolean().optional().describe("If `true`, dashboard hides edit controls.")
157
174
  }).describe(
158
- "Percentage-rollout sub-gate. Hashes `bucketBy` (default `userID`) with `salt` and returns `true` for `rolloutPct/10000` fraction of callers."
175
+ "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)."
159
176
  );
160
177
  var stackedGateEntrySchema = z3.discriminatedUnion("type", [stackedConditionSchema, stackedRolloutSchema]).describe(
161
178
  "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."
@@ -599,7 +616,12 @@ var experimentGroupSchema = z6.object({
599
616
  var paramSchemaSchema = z6.record(z6.string(), z6.enum(["string", "bool", "number"])).describe(
600
617
  "Map of param-name \u2192 scalar type. Defines the shape of `groups[].params`. Example: `{ headline: 'string', show_cta: 'bool' }`."
601
618
  );
602
- var bucketBySchema = z6.enum(["user_id", "session_id", "device_id"]).describe("Identifier used as the bucketing key. `user_id` is sticky across sessions.");
619
+ var bucketBySchema = z6.string().min(1).max(128).regex(
620
+ /^[a-zA-Z_][a-zA-Z0-9_]*$/,
621
+ "Must be a user-attribute name (letters, digits, underscore; not starting with a digit)."
622
+ ).describe(
623
+ "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."
624
+ );
603
625
  var experimentCreateSchema = z6.object({
604
626
  name: experimentNameSchema,
605
627
  description: z6.string().max(2e3).nullable().default(null).describe("Free-form description. Max 2000 chars, markdown rendered in the dashboard."),
@@ -671,7 +693,7 @@ var experimentUpdateSchema = z6.object({
671
693
  );
672
694
  var experimentStatusUpdateSchema = z6.object({
673
695
  status: z6.enum(["draft", "running", "stopped", "archived"]).describe(
674
- "Target status. Allowed transitions: `draft \u2192 running`, `running \u2192 stopped`, `stopped \u2192 archived`, `draft \u2192 archived`. Restarting an archived experiment is not allowed."
696
+ "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."
675
697
  )
676
698
  }).describe("Body for `POST /api/admin/experiments/{id}/status`.");
677
699
  var experimentMetricsUpdateSchema = z6.object({
@@ -776,24 +798,29 @@ function experimentsClient(t) {
776
798
  if (opts.cursor) query.cursor = opts.cursor;
777
799
  return t.request("GET", BASE2, void 0, query);
778
800
  }
779
- async function listAll() {
801
+ async function listAllWith(status) {
780
802
  const out = [];
781
803
  let cursor;
782
804
  do {
783
- const page = await list({ limit: 500, cursor });
805
+ const query = { limit: "500" };
806
+ if (cursor) query.cursor = cursor;
807
+ if (status) query.status = status;
808
+ const page = await t.request("GET", BASE2, void 0, query);
784
809
  out.push(...page.data);
785
810
  cursor = page.next_cursor ?? void 0;
786
811
  } while (cursor);
787
812
  return out;
788
813
  }
814
+ async function listAll() {
815
+ return listAllWith();
816
+ }
789
817
  async function resolve(idOrName) {
790
818
  try {
791
819
  return await t.request("GET", `${BASE2}/${idOrName}`);
792
820
  } catch (err) {
793
821
  if (!(err instanceof ApiError) || err.status !== 404) throw err;
794
822
  }
795
- const all = await listAll();
796
- const found = all.find((e) => e.name === idOrName);
823
+ const found = (await listAllWith()).find((e) => e.name === idOrName) ?? (await listAllWith("archived")).find((e) => e.name === idOrName);
797
824
  if (!found) throw new ApiError(`Experiment '${idOrName}' not found`, 404);
798
825
  return found;
799
826
  }
@@ -816,6 +843,7 @@ function experimentsClient(t) {
816
843
  start: (id) => setStatus(id, "running"),
817
844
  stop: (id) => setStatus(id, "stopped"),
818
845
  archive: (id) => setStatus(id, "archived"),
846
+ restore: (id) => setStatus(id, "draft"),
819
847
  setMetrics: (id, input) => t.request("POST", `${BASE2}/${id}/metrics`, experimentMetricsUpdateSchema.parse(input)),
820
848
  results: (id) => t.request("GET", `${BASE2}/${id}/results`),
821
849
  timeseries: (id, metric) => t.request(
@@ -862,7 +890,7 @@ var experimentsResource = {
862
890
  "",
863
891
  "**Identity.** Stable `name` (a-z, 0-9, `_`/`-`, max 64 chars). Immutable after create.",
864
892
  "",
865
- "**Lifecycle.** `draft \u2192 running \u2192 stopped \u2192 archived`. Transition via `POST /{id}/status`. Restarting an archived experiment is not allowed.",
893
+ "**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.",
866
894
  "",
867
895
  "**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.",
868
896
  "",
@@ -881,6 +909,7 @@ var experimentsResource = {
881
909
  { name: "start", description: "Transition draft experiment to running" },
882
910
  { name: "stop", description: "Stop a running experiment" },
883
911
  { name: "archive", description: "Archive a stopped experiment" },
912
+ { name: "restore", description: "Restore an archived (never-started) experiment to draft" },
884
913
  { name: "reanalyze", description: "Re-run analysis pass for the experiment" }
885
914
  ],
886
915
  endpoints: [
@@ -1061,8 +1090,9 @@ var experimentsResource = {
1061
1090
  "- `running \u2192 stopped` \u2014 halts allocation. Existing exposures stay in the dataset.",
1062
1091
  "- `stopped \u2192 archived` \u2014 soft-delete.",
1063
1092
  "- `draft \u2192 archived` \u2014 discard an unstarted experiment.",
1093
+ "- `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.",
1064
1094
  "",
1065
- "Restarting an `archived` experiment is not allowed; clone instead. Returns `409` on illegal transitions and `429` if the plan's `experiments_running` limit is exceeded on `\u2192 running`."
1095
+ "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`."
1066
1096
  ].join("\n"),
1067
1097
  pathParams: { id: "Stable opaque experiment id." },
1068
1098
  request: experimentStatusUpdateSchema,
@@ -1083,6 +1113,11 @@ var experimentsResource = {
1083
1113
  summary: "Archive \u2014 stopped \u2192 archived",
1084
1114
  description: "Soft-deletes the experiment. List endpoints exclude archived rows by default.",
1085
1115
  value: { status: "archived" }
1116
+ },
1117
+ restore: {
1118
+ summary: "Restore \u2014 archived \u2192 draft",
1119
+ description: "Un-deletes a soft-deleted experiment so it can be re-completed and started. Allowed only if it never ran.",
1120
+ value: { status: "draft" }
1086
1121
  }
1087
1122
  },
1088
1123
  response: { id: "exp_01j7wb12c3d4e5f6g7h8j9k0l1", status: "running" }
@@ -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[]>;
@@ -84,11 +86,7 @@ declare const experimentsResource: {
84
86
  tag: z.ZodDefault<z.ZodNullable<z.ZodString>>;
85
87
  owner_email: z.ZodDefault<z.ZodNullable<z.ZodString>>;
86
88
  audience: z.ZodDefault<z.ZodNullable<z.ZodString>>;
87
- bucket_by: z.ZodDefault<z.ZodNullable<z.ZodEnum<{
88
- user_id: "user_id";
89
- session_id: "session_id";
90
- device_id: "device_id";
91
- }>>>;
89
+ bucket_by: z.ZodDefault<z.ZodNullable<z.ZodString>>;
92
90
  folder: z.ZodOptional<z.ZodNullable<z.ZodString>>;
93
91
  universe: z.ZodString;
94
92
  targeting_gate: z.ZodDefault<z.ZodNullable<z.ZodString>>;
@@ -124,11 +122,7 @@ declare const experimentsResource: {
124
122
  tag: z.ZodOptional<z.ZodNullable<z.ZodString>>;
125
123
  owner_email: z.ZodOptional<z.ZodNullable<z.ZodString>>;
126
124
  audience: z.ZodOptional<z.ZodNullable<z.ZodString>>;
127
- bucket_by: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
128
- user_id: "user_id";
129
- session_id: "session_id";
130
- device_id: "device_id";
131
- }>>>;
125
+ bucket_by: z.ZodOptional<z.ZodNullable<z.ZodString>>;
132
126
  folder: z.ZodOptional<z.ZodNullable<z.ZodString>>;
133
127
  targeting_gate: z.ZodOptional<z.ZodNullable<z.ZodString>>;
134
128
  allocation_pct: z.ZodOptional<z.ZodNumber>;
@@ -167,6 +161,9 @@ declare const experimentsResource: {
167
161
  }, {
168
162
  readonly name: "archive";
169
163
  readonly description: "Archive a stopped experiment";
164
+ }, {
165
+ readonly name: "restore";
166
+ readonly description: "Restore an archived (never-started) experiment to draft";
170
167
  }, {
171
168
  readonly name: "reanalyze";
172
169
  readonly description: "Re-run analysis pass for the experiment";
@@ -186,11 +183,7 @@ declare const experimentsResource: {
186
183
  tag: z.ZodNullable<z.ZodString>;
187
184
  ownerEmail: z.ZodNullable<z.ZodString>;
188
185
  audience: z.ZodNullable<z.ZodString>;
189
- bucketBy: z.ZodNullable<z.ZodEnum<{
190
- user_id: "user_id";
191
- session_id: "session_id";
192
- device_id: "device_id";
193
- }>>;
186
+ bucketBy: z.ZodNullable<z.ZodString>;
194
187
  folder: z.ZodNullable<z.ZodString>;
195
188
  status: z.ZodEnum<{
196
189
  draft: "draft";
@@ -273,11 +266,7 @@ declare const experimentsResource: {
273
266
  tag: z.ZodNullable<z.ZodString>;
274
267
  ownerEmail: z.ZodNullable<z.ZodString>;
275
268
  audience: z.ZodNullable<z.ZodString>;
276
- bucketBy: z.ZodNullable<z.ZodEnum<{
277
- user_id: "user_id";
278
- session_id: "session_id";
279
- device_id: "device_id";
280
- }>>;
269
+ bucketBy: z.ZodNullable<z.ZodString>;
281
270
  folder: z.ZodNullable<z.ZodString>;
282
271
  status: z.ZodEnum<{
283
272
  draft: "draft";
@@ -352,11 +341,7 @@ declare const experimentsResource: {
352
341
  tag: z.ZodDefault<z.ZodNullable<z.ZodString>>;
353
342
  owner_email: z.ZodDefault<z.ZodNullable<z.ZodString>>;
354
343
  audience: z.ZodDefault<z.ZodNullable<z.ZodString>>;
355
- bucket_by: z.ZodDefault<z.ZodNullable<z.ZodEnum<{
356
- user_id: "user_id";
357
- session_id: "session_id";
358
- device_id: "device_id";
359
- }>>>;
344
+ bucket_by: z.ZodDefault<z.ZodNullable<z.ZodString>>;
360
345
  folder: z.ZodOptional<z.ZodNullable<z.ZodString>>;
361
346
  universe: z.ZodString;
362
347
  targeting_gate: z.ZodDefault<z.ZodNullable<z.ZodString>>;
@@ -491,11 +476,7 @@ declare const experimentsResource: {
491
476
  tag: z.ZodOptional<z.ZodNullable<z.ZodString>>;
492
477
  owner_email: z.ZodOptional<z.ZodNullable<z.ZodString>>;
493
478
  audience: z.ZodOptional<z.ZodNullable<z.ZodString>>;
494
- bucket_by: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
495
- user_id: "user_id";
496
- session_id: "session_id";
497
- device_id: "device_id";
498
- }>>>;
479
+ bucket_by: z.ZodOptional<z.ZodNullable<z.ZodString>>;
499
480
  folder: z.ZodOptional<z.ZodNullable<z.ZodString>>;
500
481
  targeting_gate: z.ZodOptional<z.ZodNullable<z.ZodString>>;
501
482
  allocation_pct: z.ZodOptional<z.ZodNumber>;
@@ -637,6 +618,13 @@ declare const experimentsResource: {
637
618
  readonly status: "archived";
638
619
  };
639
620
  };
621
+ readonly restore: {
622
+ readonly summary: "Restore — archived → draft";
623
+ readonly description: "Un-deletes a soft-deleted experiment so it can be re-completed and started. Allowed only if it never ran.";
624
+ readonly value: {
625
+ readonly status: "draft";
626
+ };
627
+ };
640
628
  };
641
629
  readonly response: {
642
630
  readonly id: "exp_01j7wb12c3d4e5f6g7h8j9k0l1";
@@ -34,7 +34,12 @@ var experimentGroupSchema = z3.object({
34
34
  var paramSchemaSchema = z3.record(z3.string(), z3.enum(["string", "bool", "number"])).describe(
35
35
  "Map of param-name \u2192 scalar type. Defines the shape of `groups[].params`. Example: `{ headline: 'string', show_cta: 'bool' }`."
36
36
  );
37
- var bucketBySchema = z3.enum(["user_id", "session_id", "device_id"]).describe("Identifier used as the bucketing key. `user_id` is sticky across sessions.");
37
+ var bucketBySchema = z3.string().min(1).max(128).regex(
38
+ /^[a-zA-Z_][a-zA-Z0-9_]*$/,
39
+ "Must be a user-attribute name (letters, digits, underscore; not starting with a digit)."
40
+ ).describe(
41
+ "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."
42
+ );
38
43
  var experimentCreateSchema = z3.object({
39
44
  name: experimentNameSchema,
40
45
  description: z3.string().max(2e3).nullable().default(null).describe("Free-form description. Max 2000 chars, markdown rendered in the dashboard."),
@@ -106,7 +111,7 @@ var experimentUpdateSchema = z3.object({
106
111
  );
107
112
  var experimentStatusUpdateSchema = z3.object({
108
113
  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`. Restarting an archived experiment is not allowed."
114
+ "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."
110
115
  )
111
116
  }).describe("Body for `POST /api/admin/experiments/{id}/status`.");
112
117
  var experimentMetricsUpdateSchema = z3.object({
@@ -221,24 +226,29 @@ function experimentsClient(t) {
221
226
  if (opts.cursor) query.cursor = opts.cursor;
222
227
  return t.request("GET", BASE, void 0, query);
223
228
  }
224
- async function listAll() {
229
+ async function listAllWith(status) {
225
230
  const out = [];
226
231
  let cursor;
227
232
  do {
228
- const page = await list({ limit: 500, cursor });
233
+ const query = { limit: "500" };
234
+ if (cursor) query.cursor = cursor;
235
+ if (status) query.status = status;
236
+ const page = await t.request("GET", BASE, void 0, query);
229
237
  out.push(...page.data);
230
238
  cursor = page.next_cursor ?? void 0;
231
239
  } while (cursor);
232
240
  return out;
233
241
  }
242
+ async function listAll() {
243
+ return listAllWith();
244
+ }
234
245
  async function resolve(idOrName) {
235
246
  try {
236
247
  return await t.request("GET", `${BASE}/${idOrName}`);
237
248
  } catch (err) {
238
249
  if (!(err instanceof ApiError) || err.status !== 404) throw err;
239
250
  }
240
- const all = await listAll();
241
- const found = all.find((e) => e.name === idOrName);
251
+ const found = (await listAllWith()).find((e) => e.name === idOrName) ?? (await listAllWith("archived")).find((e) => e.name === idOrName);
242
252
  if (!found) throw new ApiError(`Experiment '${idOrName}' not found`, 404);
243
253
  return found;
244
254
  }
@@ -261,6 +271,7 @@ function experimentsClient(t) {
261
271
  start: (id) => setStatus(id, "running"),
262
272
  stop: (id) => setStatus(id, "stopped"),
263
273
  archive: (id) => setStatus(id, "archived"),
274
+ restore: (id) => setStatus(id, "draft"),
264
275
  setMetrics: (id, input) => t.request("POST", `${BASE}/${id}/metrics`, experimentMetricsUpdateSchema.parse(input)),
265
276
  results: (id) => t.request("GET", `${BASE}/${id}/results`),
266
277
  timeseries: (id, metric) => t.request(
@@ -307,7 +318,7 @@ var experimentsResource = {
307
318
  "",
308
319
  "**Identity.** Stable `name` (a-z, 0-9, `_`/`-`, max 64 chars). Immutable after create.",
309
320
  "",
310
- "**Lifecycle.** `draft \u2192 running \u2192 stopped \u2192 archived`. Transition via `POST /{id}/status`. Restarting an archived experiment is not allowed.",
321
+ "**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.",
311
322
  "",
312
323
  "**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.",
313
324
  "",
@@ -326,6 +337,7 @@ var experimentsResource = {
326
337
  { name: "start", description: "Transition draft experiment to running" },
327
338
  { name: "stop", description: "Stop a running experiment" },
328
339
  { name: "archive", description: "Archive a stopped experiment" },
340
+ { name: "restore", description: "Restore an archived (never-started) experiment to draft" },
329
341
  { name: "reanalyze", description: "Re-run analysis pass for the experiment" }
330
342
  ],
331
343
  endpoints: [
@@ -506,8 +518,9 @@ var experimentsResource = {
506
518
  "- `running \u2192 stopped` \u2014 halts allocation. Existing exposures stay in the dataset.",
507
519
  "- `stopped \u2192 archived` \u2014 soft-delete.",
508
520
  "- `draft \u2192 archived` \u2014 discard an unstarted experiment.",
521
+ "- `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.",
509
522
  "",
510
- "Restarting an `archived` experiment is not allowed; clone instead. Returns `409` on illegal transitions and `429` if the plan's `experiments_running` limit is exceeded on `\u2192 running`."
523
+ "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`."
511
524
  ].join("\n"),
512
525
  pathParams: { id: "Stable opaque experiment id." },
513
526
  request: experimentStatusUpdateSchema,
@@ -528,6 +541,11 @@ var experimentsResource = {
528
541
  summary: "Archive \u2014 stopped \u2192 archived",
529
542
  description: "Soft-deletes the experiment. List endpoints exclude archived rows by default.",
530
543
  value: { status: "archived" }
544
+ },
545
+ restore: {
546
+ summary: "Restore \u2014 archived \u2192 draft",
547
+ description: "Un-deletes a soft-deleted experiment so it can be re-completed and started. Allowed only if it never ran.",
548
+ value: { status: "draft" }
531
549
  }
532
550
  },
533
551
  response: { id: "exp_01j7wb12c3d4e5f6g7h8j9k0l1", status: "running" }
@@ -90,6 +90,8 @@ declare const gatesResource: {
90
90
  semver_gte: "semver_gte";
91
91
  semver_lt: "semver_lt";
92
92
  semver_lte: "semver_lte";
93
+ gate_pass: "gate_pass";
94
+ exp_in: "exp_in";
93
95
  }>;
94
96
  value: z.ZodUnknown;
95
97
  }, z.core.$strip>>>;
@@ -120,9 +122,20 @@ declare const gatesResource: {
120
122
  semver_gte: "semver_gte";
121
123
  semver_lt: "semver_lt";
122
124
  semver_lte: "semver_lte";
125
+ gate_pass: "gate_pass";
126
+ exp_in: "exp_in";
123
127
  }>;
124
128
  value: z.ZodUnknown;
125
129
  }, z.core.$strip>>>;
130
+ rolloutPct: z.ZodOptional<z.ZodNumber>;
131
+ bucketBy: z.ZodOptional<z.ZodString>;
132
+ salt: z.ZodOptional<z.ZodString>;
133
+ ramp: z.ZodOptional<z.ZodObject<{
134
+ from: z.ZodNumber;
135
+ to: z.ZodNumber;
136
+ startAt: z.ZodNumber;
137
+ durationMs: z.ZodNumber;
138
+ }, z.core.$strip>>;
126
139
  locked: z.ZodOptional<z.ZodBoolean>;
127
140
  }, z.core.$strip>, z.ZodObject<{
128
141
  id: z.ZodString;
@@ -132,6 +145,12 @@ declare const gatesResource: {
132
145
  rolloutPct: z.ZodNumber;
133
146
  bucketBy: z.ZodOptional<z.ZodString>;
134
147
  salt: z.ZodOptional<z.ZodString>;
148
+ ramp: z.ZodOptional<z.ZodObject<{
149
+ from: z.ZodNumber;
150
+ to: z.ZodNumber;
151
+ startAt: z.ZodNumber;
152
+ durationMs: z.ZodNumber;
153
+ }, z.core.$strip>>;
135
154
  locked: z.ZodOptional<z.ZodBoolean>;
136
155
  }, z.core.$strip>], "type">>>>;
137
156
  }, z.core.$strip>;
@@ -160,6 +179,8 @@ declare const gatesResource: {
160
179
  semver_gte: "semver_gte";
161
180
  semver_lt: "semver_lt";
162
181
  semver_lte: "semver_lte";
182
+ gate_pass: "gate_pass";
183
+ exp_in: "exp_in";
163
184
  }>;
164
185
  value: z.ZodUnknown;
165
186
  }, z.core.$strip>>>;
@@ -190,9 +211,20 @@ declare const gatesResource: {
190
211
  semver_gte: "semver_gte";
191
212
  semver_lt: "semver_lt";
192
213
  semver_lte: "semver_lte";
214
+ gate_pass: "gate_pass";
215
+ exp_in: "exp_in";
193
216
  }>;
194
217
  value: z.ZodUnknown;
195
218
  }, z.core.$strip>>>;
219
+ rolloutPct: z.ZodOptional<z.ZodNumber>;
220
+ bucketBy: z.ZodOptional<z.ZodString>;
221
+ salt: z.ZodOptional<z.ZodString>;
222
+ ramp: z.ZodOptional<z.ZodObject<{
223
+ from: z.ZodNumber;
224
+ to: z.ZodNumber;
225
+ startAt: z.ZodNumber;
226
+ durationMs: z.ZodNumber;
227
+ }, z.core.$strip>>;
196
228
  locked: z.ZodOptional<z.ZodBoolean>;
197
229
  }, z.core.$strip>, z.ZodObject<{
198
230
  id: z.ZodString;
@@ -202,6 +234,12 @@ declare const gatesResource: {
202
234
  rolloutPct: z.ZodNumber;
203
235
  bucketBy: z.ZodOptional<z.ZodString>;
204
236
  salt: z.ZodOptional<z.ZodString>;
237
+ ramp: z.ZodOptional<z.ZodObject<{
238
+ from: z.ZodNumber;
239
+ to: z.ZodNumber;
240
+ startAt: z.ZodNumber;
241
+ durationMs: z.ZodNumber;
242
+ }, z.core.$strip>>;
205
243
  locked: z.ZodOptional<z.ZodBoolean>;
206
244
  }, z.core.$strip>], "type">>>>;
207
245
  }, z.core.$strip>;
@@ -248,6 +286,8 @@ declare const gatesResource: {
248
286
  semver_gte: "semver_gte";
249
287
  semver_lt: "semver_lt";
250
288
  semver_lte: "semver_lte";
289
+ gate_pass: "gate_pass";
290
+ exp_in: "exp_in";
251
291
  }>;
252
292
  value: z.ZodUnknown;
253
293
  }, z.core.$strip>>>;
@@ -283,9 +323,20 @@ declare const gatesResource: {
283
323
  semver_gte: "semver_gte";
284
324
  semver_lt: "semver_lt";
285
325
  semver_lte: "semver_lte";
326
+ gate_pass: "gate_pass";
327
+ exp_in: "exp_in";
286
328
  }>;
287
329
  value: z.ZodUnknown;
288
330
  }, z.core.$strip>>>;
331
+ rolloutPct: z.ZodOptional<z.ZodNumber>;
332
+ bucketBy: z.ZodOptional<z.ZodString>;
333
+ salt: z.ZodOptional<z.ZodString>;
334
+ ramp: z.ZodOptional<z.ZodObject<{
335
+ from: z.ZodNumber;
336
+ to: z.ZodNumber;
337
+ startAt: z.ZodNumber;
338
+ durationMs: z.ZodNumber;
339
+ }, z.core.$strip>>;
289
340
  locked: z.ZodOptional<z.ZodBoolean>;
290
341
  }, z.core.$strip>, z.ZodObject<{
291
342
  id: z.ZodString;
@@ -295,6 +346,12 @@ declare const gatesResource: {
295
346
  rolloutPct: z.ZodNumber;
296
347
  bucketBy: z.ZodOptional<z.ZodString>;
297
348
  salt: z.ZodOptional<z.ZodString>;
349
+ ramp: z.ZodOptional<z.ZodObject<{
350
+ from: z.ZodNumber;
351
+ to: z.ZodNumber;
352
+ startAt: z.ZodNumber;
353
+ durationMs: z.ZodNumber;
354
+ }, z.core.$strip>>;
298
355
  locked: z.ZodOptional<z.ZodBoolean>;
299
356
  }, z.core.$strip>], "type">>>>;
300
357
  updatedAt: z.ZodString;
@@ -364,6 +421,8 @@ declare const gatesResource: {
364
421
  semver_gte: "semver_gte";
365
422
  semver_lt: "semver_lt";
366
423
  semver_lte: "semver_lte";
424
+ gate_pass: "gate_pass";
425
+ exp_in: "exp_in";
367
426
  }>;
368
427
  value: z.ZodUnknown;
369
428
  }, z.core.$strip>>>;
@@ -394,9 +453,20 @@ declare const gatesResource: {
394
453
  semver_gte: "semver_gte";
395
454
  semver_lt: "semver_lt";
396
455
  semver_lte: "semver_lte";
456
+ gate_pass: "gate_pass";
457
+ exp_in: "exp_in";
397
458
  }>;
398
459
  value: z.ZodUnknown;
399
460
  }, z.core.$strip>>>;
461
+ rolloutPct: z.ZodOptional<z.ZodNumber>;
462
+ bucketBy: z.ZodOptional<z.ZodString>;
463
+ salt: z.ZodOptional<z.ZodString>;
464
+ ramp: z.ZodOptional<z.ZodObject<{
465
+ from: z.ZodNumber;
466
+ to: z.ZodNumber;
467
+ startAt: z.ZodNumber;
468
+ durationMs: z.ZodNumber;
469
+ }, z.core.$strip>>;
400
470
  locked: z.ZodOptional<z.ZodBoolean>;
401
471
  }, z.core.$strip>, z.ZodObject<{
402
472
  id: z.ZodString;
@@ -406,6 +476,12 @@ declare const gatesResource: {
406
476
  rolloutPct: z.ZodNumber;
407
477
  bucketBy: z.ZodOptional<z.ZodString>;
408
478
  salt: z.ZodOptional<z.ZodString>;
479
+ ramp: z.ZodOptional<z.ZodObject<{
480
+ from: z.ZodNumber;
481
+ to: z.ZodNumber;
482
+ startAt: z.ZodNumber;
483
+ durationMs: z.ZodNumber;
484
+ }, z.core.$strip>>;
409
485
  locked: z.ZodOptional<z.ZodBoolean>;
410
486
  }, z.core.$strip>], "type">>>>;
411
487
  }, z.core.$strip>;
@@ -531,6 +607,8 @@ declare const gatesResource: {
531
607
  semver_gte: "semver_gte";
532
608
  semver_lt: "semver_lt";
533
609
  semver_lte: "semver_lte";
610
+ gate_pass: "gate_pass";
611
+ exp_in: "exp_in";
534
612
  }>;
535
613
  value: z.ZodUnknown;
536
614
  }, z.core.$strip>>>;
@@ -561,9 +639,20 @@ declare const gatesResource: {
561
639
  semver_gte: "semver_gte";
562
640
  semver_lt: "semver_lt";
563
641
  semver_lte: "semver_lte";
642
+ gate_pass: "gate_pass";
643
+ exp_in: "exp_in";
564
644
  }>;
565
645
  value: z.ZodUnknown;
566
646
  }, z.core.$strip>>>;
647
+ rolloutPct: z.ZodOptional<z.ZodNumber>;
648
+ bucketBy: z.ZodOptional<z.ZodString>;
649
+ salt: z.ZodOptional<z.ZodString>;
650
+ ramp: z.ZodOptional<z.ZodObject<{
651
+ from: z.ZodNumber;
652
+ to: z.ZodNumber;
653
+ startAt: z.ZodNumber;
654
+ durationMs: z.ZodNumber;
655
+ }, z.core.$strip>>;
567
656
  locked: z.ZodOptional<z.ZodBoolean>;
568
657
  }, z.core.$strip>, z.ZodObject<{
569
658
  id: z.ZodString;
@@ -573,6 +662,12 @@ declare const gatesResource: {
573
662
  rolloutPct: z.ZodNumber;
574
663
  bucketBy: z.ZodOptional<z.ZodString>;
575
664
  salt: z.ZodOptional<z.ZodString>;
665
+ ramp: z.ZodOptional<z.ZodObject<{
666
+ from: z.ZodNumber;
667
+ to: z.ZodNumber;
668
+ startAt: z.ZodNumber;
669
+ durationMs: z.ZodNumber;
670
+ }, z.core.$strip>>;
576
671
  locked: z.ZodOptional<z.ZodBoolean>;
577
672
  }, z.core.$strip>], "type">>>>;
578
673
  }, z.core.$strip>;