@shipeasy/openapi 1.2.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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."),
@@ -2343,6 +2365,13 @@ var killswitchesResource = {
2343
2365
  import { z as z14 } from "zod";
2344
2366
  var ALERT_COMPARATORS = ["gt", "gte", "lt", "lte"];
2345
2367
  var ALERT_SEVERITIES = ["danger", "warn", "info"];
2368
+ var notificationTargetSchema = z14.object({
2369
+ slackChannel: z14.object({
2370
+ id: z14.string().min(1).describe("Slack channel id (e.g. C0123ABCD)."),
2371
+ name: z14.string().min(1).describe("Slack channel name (without the leading #).")
2372
+ }).nullish().describe("Slack channel to post this rule's alert to. Requires a Slack connector."),
2373
+ email: z14.string().email().nullish().describe("Email address to notify for this rule (overrides the default recipient).")
2374
+ }).strict();
2346
2375
  var alertRuleCreateSchema = z14.object({
2347
2376
  name: z14.string().min(1).max(120).describe("Human label for the rule, shown on the alert and the rules list."),
2348
2377
  metricId: z14.string().min(1).describe("Id of the metric to evaluate."),
@@ -2350,7 +2379,8 @@ var alertRuleCreateSchema = z14.object({
2350
2379
  threshold: z14.number().finite().describe("Threshold the metric value is compared against."),
2351
2380
  windowHours: z14.number().int().min(1).max(720).default(24).describe("Lookback window (hours) the metric is aggregated over. 1\u2013720."),
2352
2381
  severity: z14.enum(ALERT_SEVERITIES).default("warn").describe("Severity of the raised alert."),
2353
- enabled: z14.boolean().default(true).describe("Whether the rule is evaluated by the cron.")
2382
+ enabled: z14.boolean().default(true).describe("Whether the rule is evaluated by the cron."),
2383
+ notify: notificationTargetSchema.nullish().describe("Where to deliver this rule's alert (Slack channel and/or email target).")
2354
2384
  }).strict();
2355
2385
  var alertRuleUpdateSchema = z14.object({
2356
2386
  name: z14.string().min(1).max(120),
@@ -2358,7 +2388,8 @@ var alertRuleUpdateSchema = z14.object({
2358
2388
  threshold: z14.number().finite(),
2359
2389
  windowHours: z14.number().int().min(1).max(720),
2360
2390
  severity: z14.enum(ALERT_SEVERITIES),
2361
- enabled: z14.boolean()
2391
+ enabled: z14.boolean(),
2392
+ notify: notificationTargetSchema.nullish()
2362
2393
  }).partial().strict();
2363
2394
 
2364
2395
  // src/resources/alert-rules.ts
@@ -1,5 +1,5 @@
1
1
  import { z } from 'zod';
2
- import { alertRuleCreateSchema, alertRuleUpdateSchema } from '../schemas/alert-rules.js';
2
+ import { NotificationTarget, alertRuleCreateSchema, alertRuleUpdateSchema } from '../schemas/alert-rules.js';
3
3
  import { Transport } from '../transport.js';
4
4
 
5
5
  type AlertRuleCreateInput = z.input<typeof alertRuleCreateSchema>;
@@ -24,6 +24,8 @@ interface AlertRule {
24
24
  windowHours: number;
25
25
  severity: "danger" | "warn" | "info";
26
26
  enabled: boolean;
27
+ /** Delivery target for this rule's alert; null = use project default. */
28
+ notify: NotificationTarget | null;
27
29
  createdAt: string;
28
30
  updatedAt: string;
29
31
  }
@@ -2,6 +2,13 @@
2
2
  import { z } from "zod";
3
3
  var ALERT_COMPARATORS = ["gt", "gte", "lt", "lte"];
4
4
  var ALERT_SEVERITIES = ["danger", "warn", "info"];
5
+ var notificationTargetSchema = z.object({
6
+ slackChannel: z.object({
7
+ id: z.string().min(1).describe("Slack channel id (e.g. C0123ABCD)."),
8
+ name: z.string().min(1).describe("Slack channel name (without the leading #).")
9
+ }).nullish().describe("Slack channel to post this rule's alert to. Requires a Slack connector."),
10
+ email: z.string().email().nullish().describe("Email address to notify for this rule (overrides the default recipient).")
11
+ }).strict();
5
12
  var alertRuleCreateSchema = z.object({
6
13
  name: z.string().min(1).max(120).describe("Human label for the rule, shown on the alert and the rules list."),
7
14
  metricId: z.string().min(1).describe("Id of the metric to evaluate."),
@@ -9,7 +16,8 @@ var alertRuleCreateSchema = z.object({
9
16
  threshold: z.number().finite().describe("Threshold the metric value is compared against."),
10
17
  windowHours: z.number().int().min(1).max(720).default(24).describe("Lookback window (hours) the metric is aggregated over. 1\u2013720."),
11
18
  severity: z.enum(ALERT_SEVERITIES).default("warn").describe("Severity of the raised alert."),
12
- enabled: z.boolean().default(true).describe("Whether the rule is evaluated by the cron.")
19
+ enabled: z.boolean().default(true).describe("Whether the rule is evaluated by the cron."),
20
+ notify: notificationTargetSchema.nullish().describe("Where to deliver this rule's alert (Slack channel and/or email target).")
13
21
  }).strict();
14
22
  var alertRuleUpdateSchema = z.object({
15
23
  name: z.string().min(1).max(120),
@@ -17,7 +25,8 @@ var alertRuleUpdateSchema = z.object({
17
25
  threshold: z.number().finite(),
18
26
  windowHours: z.number().int().min(1).max(720),
19
27
  severity: z.enum(ALERT_SEVERITIES),
20
- enabled: z.boolean()
28
+ enabled: z.boolean(),
29
+ notify: notificationTargetSchema.nullish()
21
30
  }).partial().strict();
22
31
 
23
32
  // src/transport.ts
@@ -86,11 +86,7 @@ declare const experimentsResource: {
86
86
  tag: z.ZodDefault<z.ZodNullable<z.ZodString>>;
87
87
  owner_email: z.ZodDefault<z.ZodNullable<z.ZodString>>;
88
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
- }>>>;
89
+ bucket_by: z.ZodDefault<z.ZodNullable<z.ZodString>>;
94
90
  folder: z.ZodOptional<z.ZodNullable<z.ZodString>>;
95
91
  universe: z.ZodString;
96
92
  targeting_gate: z.ZodDefault<z.ZodNullable<z.ZodString>>;
@@ -126,11 +122,7 @@ declare const experimentsResource: {
126
122
  tag: z.ZodOptional<z.ZodNullable<z.ZodString>>;
127
123
  owner_email: z.ZodOptional<z.ZodNullable<z.ZodString>>;
128
124
  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
- }>>>;
125
+ bucket_by: z.ZodOptional<z.ZodNullable<z.ZodString>>;
134
126
  folder: z.ZodOptional<z.ZodNullable<z.ZodString>>;
135
127
  targeting_gate: z.ZodOptional<z.ZodNullable<z.ZodString>>;
136
128
  allocation_pct: z.ZodOptional<z.ZodNumber>;
@@ -191,11 +183,7 @@ declare const experimentsResource: {
191
183
  tag: z.ZodNullable<z.ZodString>;
192
184
  ownerEmail: z.ZodNullable<z.ZodString>;
193
185
  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
- }>>;
186
+ bucketBy: z.ZodNullable<z.ZodString>;
199
187
  folder: z.ZodNullable<z.ZodString>;
200
188
  status: z.ZodEnum<{
201
189
  draft: "draft";
@@ -278,11 +266,7 @@ declare const experimentsResource: {
278
266
  tag: z.ZodNullable<z.ZodString>;
279
267
  ownerEmail: z.ZodNullable<z.ZodString>;
280
268
  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
- }>>;
269
+ bucketBy: z.ZodNullable<z.ZodString>;
286
270
  folder: z.ZodNullable<z.ZodString>;
287
271
  status: z.ZodEnum<{
288
272
  draft: "draft";
@@ -357,11 +341,7 @@ declare const experimentsResource: {
357
341
  tag: z.ZodDefault<z.ZodNullable<z.ZodString>>;
358
342
  owner_email: z.ZodDefault<z.ZodNullable<z.ZodString>>;
359
343
  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
- }>>>;
344
+ bucket_by: z.ZodDefault<z.ZodNullable<z.ZodString>>;
365
345
  folder: z.ZodOptional<z.ZodNullable<z.ZodString>>;
366
346
  universe: z.ZodString;
367
347
  targeting_gate: z.ZodDefault<z.ZodNullable<z.ZodString>>;
@@ -496,11 +476,7 @@ declare const experimentsResource: {
496
476
  tag: z.ZodOptional<z.ZodNullable<z.ZodString>>;
497
477
  owner_email: z.ZodOptional<z.ZodNullable<z.ZodString>>;
498
478
  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
- }>>>;
479
+ bucket_by: z.ZodOptional<z.ZodNullable<z.ZodString>>;
504
480
  folder: z.ZodOptional<z.ZodNullable<z.ZodString>>;
505
481
  targeting_gate: z.ZodOptional<z.ZodNullable<z.ZodString>>;
506
482
  allocation_pct: z.ZodOptional<z.ZodNumber>;
@@ -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."),
@@ -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>;
@@ -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
- "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)."
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
- "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`."
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 matched, evaluation short-circuits and the gate returns `true` for the current caller."
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."