@shipeasy/openapi 1.6.0 → 2.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/README.md +37 -0
- package/dist/chunk-643YSMLU.js +1616 -0
- package/dist/chunk-643YSMLU.js.map +1 -0
- package/dist/chunk-MRW3DJ57.js +3385 -0
- package/dist/chunk-MRW3DJ57.js.map +1 -0
- package/dist/client-K0ShmsC7.d.ts +11284 -0
- package/dist/client.d.ts +1 -0
- package/dist/client.js +215 -0
- package/dist/client.js.map +1 -0
- package/dist/custom.d.ts +86 -0
- package/dist/custom.js +300 -0
- package/dist/custom.js.map +1 -0
- package/dist/index.d.ts +29 -41
- package/dist/index.js +258 -4375
- package/dist/index.js.map +1 -0
- package/dist/node-context.d.ts +81 -33
- package/dist/node-context.js +98 -44
- package/dist/node-context.js.map +1 -0
- package/dist/schemas.d.ts +7973 -0
- package/dist/schemas.js +1099 -0
- package/dist/schemas.js.map +1 -0
- package/openapi.json +15922 -3232
- package/openapi.yaml +14277 -0
- package/package.json +33 -26
- package/spec/components/errors.yaml +53 -0
- package/spec/components/parameters.yaml +32 -0
- package/spec/components/responses.yaml +122 -0
- package/spec/components/schemas/alert-rules.yaml +175 -0
- package/spec/components/schemas/attributes.yaml +157 -0
- package/spec/components/schemas/common.yaml +92 -0
- package/spec/components/schemas/configs.yaml +371 -0
- package/spec/components/schemas/connectors.yaml +587 -0
- package/spec/components/schemas/errors-api.yaml +209 -0
- package/spec/components/schemas/events.yaml +321 -0
- package/spec/components/schemas/experiments.yaml +865 -0
- package/spec/components/schemas/gate-templates.yaml +237 -0
- package/spec/components/schemas/gates.yaml +890 -0
- package/spec/components/schemas/i18n.yaml +478 -0
- package/spec/components/schemas/keys.yaml +186 -0
- package/spec/components/schemas/killswitches.yaml +309 -0
- package/spec/components/schemas/metrics.yaml +547 -0
- package/spec/components/schemas/ops.yaml +1013 -0
- package/spec/components/schemas/projects.yaml +222 -0
- package/spec/components/schemas/search.yaml +46 -0
- package/spec/components/schemas/universes.yaml +127 -0
- package/spec/openapi.yaml +367 -0
- package/spec/paths/alert-rules.yaml +258 -0
- package/spec/paths/attributes.yaml +272 -0
- package/spec/paths/configs.yaml +758 -0
- package/spec/paths/connectors.yaml +782 -0
- package/spec/paths/errors.yaml +382 -0
- package/spec/paths/events.yaml +380 -0
- package/spec/paths/experiments.yaml +803 -0
- package/spec/paths/gate-templates.yaml +267 -0
- package/spec/paths/gates.yaml +465 -0
- package/spec/paths/i18n.yaml +557 -0
- package/spec/paths/keys.yaml +182 -0
- package/spec/paths/killswitches.yaml +574 -0
- package/spec/paths/metrics.yaml +313 -0
- package/spec/paths/ops.yaml +446 -0
- package/spec/paths/projects.yaml +210 -0
- package/spec/paths/search.yaml +62 -0
- package/spec/paths/universes.yaml +255 -0
- package/dist/operations/attributes.d.ts +0 -33
- package/dist/operations/attributes.js +0 -16
- package/dist/operations/cli-adapter.d.ts +0 -35
- package/dist/operations/cli-adapter.js +0 -91
- package/dist/operations/coerce.d.ts +0 -32
- package/dist/operations/coerce.js +0 -53
- package/dist/operations/configs.d.ts +0 -28
- package/dist/operations/configs.js +0 -119
- package/dist/operations/docs.d.ts +0 -37
- package/dist/operations/docs.js +0 -39
- package/dist/operations/events.d.ts +0 -28
- package/dist/operations/events.js +0 -121
- package/dist/operations/experiments.d.ts +0 -28
- package/dist/operations/experiments.js +0 -280
- package/dist/operations/gates.d.ts +0 -28
- package/dist/operations/gates.js +0 -132
- package/dist/operations/i18n.d.ts +0 -28
- package/dist/operations/i18n.js +0 -121
- package/dist/operations/index.d.ts +0 -63
- package/dist/operations/index.js +0 -1751
- package/dist/operations/killswitches.d.ts +0 -28
- package/dist/operations/killswitches.js +0 -126
- package/dist/operations/mcp-adapter.d.ts +0 -35
- package/dist/operations/mcp-adapter.js +0 -94
- package/dist/operations/metrics.d.ts +0 -30
- package/dist/operations/metrics.js +0 -114
- package/dist/operations/ops.d.ts +0 -28
- package/dist/operations/ops.js +0 -313
- package/dist/operations/projects.d.ts +0 -28
- package/dist/operations/projects.js +0 -36
- package/dist/operations/sdk-docs.d.ts +0 -28
- package/dist/operations/sdk-docs.js +0 -160
- package/dist/operations/types.d.ts +0 -138
- package/dist/operations/types.js +0 -9
- package/dist/operations/universes.d.ts +0 -28
- package/dist/operations/universes.js +0 -93
- package/dist/operations/util.d.ts +0 -33
- package/dist/operations/util.js +0 -9
- package/dist/resources/alert-rules.d.ts +0 -50
- package/dist/resources/alert-rules.js +0 -75
- package/dist/resources/attributes.d.ts +0 -19
- package/dist/resources/attributes.js +0 -10
- package/dist/resources/configs.d.ts +0 -681
- package/dist/resources/configs.js +0 -509
- package/dist/resources/events.d.ts +0 -61
- package/dist/resources/events.js +0 -42
- package/dist/resources/experiments.d.ts +0 -849
- package/dist/resources/experiments.js +0 -707
- package/dist/resources/gates.d.ts +0 -808
- package/dist/resources/gates.js +0 -522
- package/dist/resources/i18n.d.ts +0 -84
- package/dist/resources/i18n.js +0 -60
- package/dist/resources/index.d.ts +0 -2767
- package/dist/resources/index.js +0 -2563
- package/dist/resources/killswitches.d.ts +0 -436
- package/dist/resources/killswitches.js +0 -514
- package/dist/resources/metrics.d.ts +0 -54
- package/dist/resources/metrics.js +0 -40
- package/dist/resources/ops.d.ts +0 -88
- package/dist/resources/ops.js +0 -36
- package/dist/resources/projects.d.ts +0 -54
- package/dist/resources/projects.js +0 -14
- package/dist/resources/universes.d.ts +0 -185
- package/dist/resources/universes.js +0 -234
- package/dist/schemas/alert-rules.d.ts +0 -65
- package/dist/schemas/alert-rules.js +0 -37
- package/dist/schemas/configs.d.ts +0 -154
- package/dist/schemas/configs.js +0 -134
- package/dist/schemas/constants.d.ts +0 -6
- package/dist/schemas/constants.js +0 -7
- package/dist/schemas/experiments.d.ts +0 -229
- package/dist/schemas/experiments.js +0 -221
- package/dist/schemas/folder.d.ts +0 -10
- package/dist/schemas/folder.js +0 -8
- package/dist/schemas/gates.d.ts +0 -386
- package/dist/schemas/gates.js +0 -190
- package/dist/schemas/index.d.ts +0 -11
- package/dist/schemas/index.js +0 -762
- package/dist/schemas/killswitches.d.ts +0 -110
- package/dist/schemas/killswitches.js +0 -226
- package/dist/schemas/metric-name.d.ts +0 -5
- package/dist/schemas/metric-name.js +0 -6
- package/dist/schemas/pagination.d.ts +0 -37
- package/dist/schemas/pagination.js +0 -44
- package/dist/schemas/universes.d.ts +0 -35
- package/dist/schemas/universes.js +0 -55
- package/dist/transport.d.ts +0 -30
- package/dist/transport.js +0 -44
package/dist/schemas/index.js
DELETED
|
@@ -1,762 +0,0 @@
|
|
|
1
|
-
// src/schemas/constants.ts
|
|
2
|
-
var CONFIG_ENVS = ["dev", "staging", "prod"];
|
|
3
|
-
var CONFIG_KINDS = ["config", "killswitch"];
|
|
4
|
-
|
|
5
|
-
// src/schemas/pagination.ts
|
|
6
|
-
import { z } from "zod";
|
|
7
|
-
var DEFAULT_PAGE_SIZE = 100;
|
|
8
|
-
var MAX_PAGE_SIZE = 500;
|
|
9
|
-
var pageQuerySchema = z.object({
|
|
10
|
-
limit: z.coerce.number().int().min(1).max(MAX_PAGE_SIZE).default(DEFAULT_PAGE_SIZE),
|
|
11
|
-
cursor: z.string().min(1).optional()
|
|
12
|
-
});
|
|
13
|
-
var SEP = "|";
|
|
14
|
-
function encodeCursor(parts) {
|
|
15
|
-
if (parts.ts.includes(SEP)) {
|
|
16
|
-
throw new Error(`pagination: ts must not contain '${SEP}'`);
|
|
17
|
-
}
|
|
18
|
-
return base64UrlEncode(`${parts.ts}${SEP}${parts.id}`);
|
|
19
|
-
}
|
|
20
|
-
function decodeCursor(cursor) {
|
|
21
|
-
let raw;
|
|
22
|
-
try {
|
|
23
|
-
raw = base64UrlDecode(cursor);
|
|
24
|
-
} catch {
|
|
25
|
-
throw new Error("pagination: cursor is not valid base64url");
|
|
26
|
-
}
|
|
27
|
-
const sep = raw.indexOf(SEP);
|
|
28
|
-
if (sep < 0) throw new Error("pagination: cursor missing separator");
|
|
29
|
-
const ts = raw.slice(0, sep);
|
|
30
|
-
const id = raw.slice(sep + 1);
|
|
31
|
-
if (!ts || !id) throw new Error("pagination: cursor parts empty");
|
|
32
|
-
return { ts, id };
|
|
33
|
-
}
|
|
34
|
-
function base64UrlEncode(s) {
|
|
35
|
-
const b64 = btoa(s);
|
|
36
|
-
return b64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
37
|
-
}
|
|
38
|
-
function base64UrlDecode(s) {
|
|
39
|
-
const pad = s.length % 4 === 0 ? "" : "=".repeat(4 - s.length % 4);
|
|
40
|
-
return atob(s.replace(/-/g, "+").replace(/_/g, "/") + pad);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// src/schemas/folder.ts
|
|
44
|
-
import { z as z2 } from "zod";
|
|
45
|
-
var folderSchema = z2.string().max(256).regex(/^[a-zA-Z0-9_-]+$/, "Folder must be alphanumeric, `_` or `-` (no `/`).").nullable().optional().describe(
|
|
46
|
-
"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."
|
|
47
|
-
);
|
|
48
|
-
|
|
49
|
-
// src/schemas/metric-name.ts
|
|
50
|
-
import { z as z3 } from "zod";
|
|
51
|
-
var metricNameSchema = z3.string().regex(/^[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?)?$/).max(128);
|
|
52
|
-
|
|
53
|
-
// src/schemas/configs.ts
|
|
54
|
-
import { z as z4 } from "zod";
|
|
55
|
-
var SEGMENT = /^[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?$/;
|
|
56
|
-
var RESERVED_FOLDER = "_default";
|
|
57
|
-
var configNameSchema = z4.string().max(128).refine(
|
|
58
|
-
(s) => {
|
|
59
|
-
const parts = s.split(".");
|
|
60
|
-
if (parts.length !== 2) return false;
|
|
61
|
-
return parts.every((p, i) => p === RESERVED_FOLDER && i === 0 ? true : SEGMENT.test(p));
|
|
62
|
-
},
|
|
63
|
-
{
|
|
64
|
-
message: "Must be `folder.name` \u2014 exactly two lowercase segments separated by a dot (e.g. `pricing.tiers`)."
|
|
65
|
-
}
|
|
66
|
-
).describe(
|
|
67
|
-
"Stable config key in `folder.name` form (two lowercase segments separated by a dot, e.g. `pricing.tiers`). Used by SDKs as `Shipeasy.getConfig('<name>')`. Immutable after create."
|
|
68
|
-
);
|
|
69
|
-
var configKindSchema = z4.enum(CONFIG_KINDS);
|
|
70
|
-
var configEnvSchema = z4.enum(CONFIG_ENVS).describe("Target environment. One of the project's configured envs (`dev`, `stage`, `prod`).");
|
|
71
|
-
var jsonSchemaSchema = z4.record(z4.string(), z4.unknown()).refine((s) => s.type === "object", { message: "Top-level schema type must be 'object'" }).describe(
|
|
72
|
-
"JSON Schema (draft 2020-12) describing the shape of the config value. Top-level `type` must be `'object'`; every published value is validated against this schema."
|
|
73
|
-
);
|
|
74
|
-
var initialValuesSchema = z4.union([z4.unknown(), z4.record(configEnvSchema, z4.unknown())]).describe(
|
|
75
|
-
"Initial config value. Either a single JSON object applied to every env, or a `{ env: value }` map seeding per-env values. Must match `schema`. Defaults to `{}` on every env when omitted."
|
|
76
|
-
);
|
|
77
|
-
var configCreateSchema = z4.object({
|
|
78
|
-
name: configNameSchema,
|
|
79
|
-
description: z4.string().max(512).optional().describe("Optional free-form description shown in the dashboard. Max 512 chars."),
|
|
80
|
-
folder: folderSchema,
|
|
81
|
-
schema: jsonSchemaSchema,
|
|
82
|
-
value: initialValuesSchema.optional()
|
|
83
|
-
}).describe("Body for `POST /api/admin/configs`. `name` + `schema` required.");
|
|
84
|
-
var configUpdateSchema = z4.object({
|
|
85
|
-
schema: jsonSchemaSchema.optional().describe(
|
|
86
|
-
"Replacement schema. When supplied, the new schema is validated against every published value before it lands."
|
|
87
|
-
),
|
|
88
|
-
value: z4.unknown().optional().describe(
|
|
89
|
-
"Flat value applied to **every** env. Publishes a new version per env. To target one env, use `PUT /{id}/drafts` then `POST /{id}/publish`."
|
|
90
|
-
),
|
|
91
|
-
folder: folderSchema
|
|
92
|
-
}).describe(
|
|
93
|
-
"Body for `PATCH /api/admin/configs/{id}`. Partial \u2014 only supplied fields change. `value` republishes on every env."
|
|
94
|
-
);
|
|
95
|
-
var configSchemaUpdateSchema = z4.object({ schema: jsonSchemaSchema }).describe("Schema-only update. Does not bump value versions or rebuild KV.");
|
|
96
|
-
var configDraftUpsertSchema = z4.object({
|
|
97
|
-
env: configEnvSchema,
|
|
98
|
-
value: z4.unknown().describe("Draft value to stage on `env`. Validated against the config's current schema.")
|
|
99
|
-
}).describe(
|
|
100
|
-
"Body for `PUT /api/admin/configs/{id}/drafts`. Stages a value for a single env without publishing."
|
|
101
|
-
);
|
|
102
|
-
var configPublishSchema = z4.object({ env: configEnvSchema }).describe(
|
|
103
|
-
"Body for `POST /api/admin/configs/{id}/publish` and `DELETE /api/admin/configs/{id}/drafts`. Names the target env."
|
|
104
|
-
);
|
|
105
|
-
var configEnvPublishedSchema = z4.object({
|
|
106
|
-
version: z4.number().int().describe("Monotonically increasing version per env."),
|
|
107
|
-
publishedAt: z4.string().describe("ISO-8601 publish timestamp."),
|
|
108
|
-
publishedBy: z4.string().describe("Email/id of the actor who published this version.")
|
|
109
|
-
});
|
|
110
|
-
var configDraftSummarySchema = z4.object({
|
|
111
|
-
updatedAt: z4.string().describe("ISO-8601 last-edit timestamp on the draft."),
|
|
112
|
-
authorEmail: z4.string().describe("Email/id of the actor who last edited the draft."),
|
|
113
|
-
baseVersion: z4.number().int().describe("Published version the draft was forked from.")
|
|
114
|
-
});
|
|
115
|
-
var configResponseSchema = z4.object({
|
|
116
|
-
id: z4.string().describe("Stable opaque config id (`cfg_\u2026`)."),
|
|
117
|
-
name: configNameSchema,
|
|
118
|
-
description: z4.string().nullable(),
|
|
119
|
-
schema: jsonSchemaSchema,
|
|
120
|
-
updatedAt: z4.string().describe("ISO-8601 timestamp of last mutation."),
|
|
121
|
-
envs: z4.record(configEnvSchema, configEnvPublishedSchema).describe("Per-env latest published version metadata."),
|
|
122
|
-
drafts: z4.record(configEnvSchema, configDraftSummarySchema).describe("Per-env active drafts (if any)."),
|
|
123
|
-
values: z4.record(configEnvSchema, z4.unknown()).optional().describe("Per-env latest published values (only returned by `GET /{id}`, not list)."),
|
|
124
|
-
draftValues: z4.record(configEnvSchema, z4.unknown()).optional().describe("Per-env draft values (only returned by `GET /{id}`).")
|
|
125
|
-
});
|
|
126
|
-
var configCreateResponseSchema = z4.object({
|
|
127
|
-
id: z4.string().describe("Newly assigned config id."),
|
|
128
|
-
name: configNameSchema
|
|
129
|
-
});
|
|
130
|
-
var configUpdateResponseSchema = z4.object({
|
|
131
|
-
id: z4.string().describe("Config id that was updated.")
|
|
132
|
-
});
|
|
133
|
-
var configDeleteResponseSchema = z4.object({ ok: z4.literal(true) });
|
|
134
|
-
var configDraftSaveResponseSchema = z4.object({
|
|
135
|
-
id: z4.string(),
|
|
136
|
-
env: configEnvSchema,
|
|
137
|
-
baseVersion: z4.number().int().describe("Published version the draft is based on."),
|
|
138
|
-
updatedAt: z4.string()
|
|
139
|
-
});
|
|
140
|
-
var configDraftDiscardResponseSchema = z4.object({ ok: z4.literal(true) });
|
|
141
|
-
var configPublishResponseSchema = z4.object({
|
|
142
|
-
id: z4.string(),
|
|
143
|
-
env: configEnvSchema,
|
|
144
|
-
version: z4.number().int().describe("Newly published version on `env`.")
|
|
145
|
-
});
|
|
146
|
-
var configActivityEntrySchema = z4.object({
|
|
147
|
-
id: z4.string(),
|
|
148
|
-
action: z4.string().describe("Audit action (e.g. `config.create`, `config.publish`)."),
|
|
149
|
-
actorEmail: z4.string(),
|
|
150
|
-
actorType: z4.enum(["user", "cli", "system"]),
|
|
151
|
-
payload: z4.unknown().nullable(),
|
|
152
|
-
createdAt: z4.string().describe("ISO-8601 timestamp.")
|
|
153
|
-
});
|
|
154
|
-
var configActivityResponseSchema = z4.array(configActivityEntrySchema).describe("Recent audit rows for one config, newest first.");
|
|
155
|
-
|
|
156
|
-
// src/schemas/gates.ts
|
|
157
|
-
import { z as z5 } from "zod";
|
|
158
|
-
var gateRuleSchema = z5.object({
|
|
159
|
-
attr: z5.string().min(1).describe(
|
|
160
|
-
"Attribute key on the evaluation context (e.g. `country`, `plan`, `email`). Matched case-sensitively against `Shipeasy.checkGate(user, name)` input."
|
|
161
|
-
),
|
|
162
|
-
op: z5.enum([
|
|
163
|
-
"eq",
|
|
164
|
-
"neq",
|
|
165
|
-
"in",
|
|
166
|
-
"not_in",
|
|
167
|
-
"gt",
|
|
168
|
-
"gte",
|
|
169
|
-
"lt",
|
|
170
|
-
"lte",
|
|
171
|
-
"contains",
|
|
172
|
-
"regex",
|
|
173
|
-
"semver_gt",
|
|
174
|
-
"semver_gte",
|
|
175
|
-
"semver_lt",
|
|
176
|
-
"semver_lte",
|
|
177
|
-
"gate_pass",
|
|
178
|
-
"exp_in"
|
|
179
|
-
]).describe(
|
|
180
|
-
'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).'
|
|
181
|
-
),
|
|
182
|
-
value: z5.unknown().describe(
|
|
183
|
-
'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`.'
|
|
184
|
-
)
|
|
185
|
-
}).describe(
|
|
186
|
-
"Single targeting predicate. A gate's `rules` array is evaluated with AND semantics; use the gatekeeper `stack` for OR / fall-through logic."
|
|
187
|
-
);
|
|
188
|
-
var gateNameSchema = z5.string().regex(
|
|
189
|
-
/^[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?)?$/,
|
|
190
|
-
"lowercase, digits, _/-; optional folder.name shape"
|
|
191
|
-
).max(128).describe(
|
|
192
|
-
"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."
|
|
193
|
-
);
|
|
194
|
-
var rampSchema = z5.object({
|
|
195
|
-
from: z5.number().int().min(0).max(1e4).describe("Start of the ramp in basis points (0\u201310000)."),
|
|
196
|
-
to: z5.number().int().min(0).max(1e4).describe("End of the ramp in basis points (0\u201310000)."),
|
|
197
|
-
startAt: z5.number().int().nonnegative().describe("Ramp start, epoch milliseconds (UTC)."),
|
|
198
|
-
durationMs: z5.number().int().positive().describe("Ramp length in milliseconds.")
|
|
199
|
-
}).describe(
|
|
200
|
-
"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."
|
|
201
|
-
);
|
|
202
|
-
var stackedConditionSchema = z5.object({
|
|
203
|
-
id: z5.string().min(1).describe(
|
|
204
|
-
"Client-supplied stable id for the stack entry (used for React keys, audit diffs, and as the default per-condition rollout salt)."
|
|
205
|
-
),
|
|
206
|
-
type: z5.literal("condition").describe("Discriminator. `condition` = rule-based predicate sub-gate."),
|
|
207
|
-
name: z5.string().optional().describe("Human label shown in the dashboard."),
|
|
208
|
-
fromTemplate: z5.string().nullable().optional().describe(
|
|
209
|
-
"Id of the project template this entry was seeded from, or `null` if hand-authored."
|
|
210
|
-
),
|
|
211
|
-
pass: z5.enum(["all", "any"]).optional().describe("Combinator across `rules`. `all` = AND (default), `any` = OR."),
|
|
212
|
-
rules: z5.array(gateRuleSchema).default([]).describe("Predicates evaluated under `pass`. Empty array = match nothing."),
|
|
213
|
-
rolloutPct: z5.number().int().min(0).max(1e4).optional().describe(
|
|
214
|
-
"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)."
|
|
215
|
-
),
|
|
216
|
-
bucketBy: z5.string().optional().describe("Attribute used to hash the caller into the per-condition bucket. Default `userID`."),
|
|
217
|
-
salt: z5.string().optional().describe("Per-condition hash salt. Defaults to the entry `id`, then the gate salt."),
|
|
218
|
-
ramp: rampSchema.optional(),
|
|
219
|
-
locked: z5.boolean().optional().describe("If `true`, dashboard hides edit controls (e.g. the trailing public-floor entry).")
|
|
220
|
-
}).describe(
|
|
221
|
-
"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."
|
|
222
|
-
);
|
|
223
|
-
var stackedRolloutSchema = z5.object({
|
|
224
|
-
id: z5.string().min(1).describe("Client-supplied stable id for the stack entry."),
|
|
225
|
-
type: z5.literal("rollout").describe("Discriminator. `rollout` = percentage-bucket sub-gate."),
|
|
226
|
-
name: z5.string().optional().describe("Human label shown in the dashboard."),
|
|
227
|
-
fromTemplate: z5.string().nullable().optional().describe("Source template id, or `null`."),
|
|
228
|
-
rolloutPct: z5.number().int().min(0).max(1e4).describe(
|
|
229
|
-
"Bucket size in basis points (0\u201310000 = 0%\u2013100%). `5000` = 50%, `100` = 1%, allows sub-1% precision."
|
|
230
|
-
),
|
|
231
|
-
bucketBy: z5.string().optional().describe(
|
|
232
|
-
"Attribute used to hash the caller into a bucket. Defaults to `user.userID`. Use e.g. `accountID` to bucket whole accounts together."
|
|
233
|
-
),
|
|
234
|
-
salt: z5.string().optional().describe(
|
|
235
|
-
"Per-entry hash salt. Defaults to the gate's salt. Set explicitly to keep two rollouts independent or correlated across gates."
|
|
236
|
-
),
|
|
237
|
-
ramp: rampSchema.optional(),
|
|
238
|
-
locked: z5.boolean().optional().describe("If `true`, dashboard hides edit controls.")
|
|
239
|
-
}).describe(
|
|
240
|
-
"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)."
|
|
241
|
-
);
|
|
242
|
-
var stackedGateEntrySchema = z5.discriminatedUnion("type", [stackedConditionSchema, stackedRolloutSchema]).describe(
|
|
243
|
-
"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."
|
|
244
|
-
);
|
|
245
|
-
var gateMetadataFields = {
|
|
246
|
-
title: z5.string().max(140).optional().describe("Human-readable title shown in the dashboard. Free-form, no key format constraint."),
|
|
247
|
-
description: z5.string().max(2e3).optional().describe("Long-form description / runbook. Markdown is rendered in the dashboard."),
|
|
248
|
-
folder: folderSchema,
|
|
249
|
-
group: z5.string().max(64).optional().describe("Group label for dashboard organisation (e.g. team or product area)."),
|
|
250
|
-
owner_email: z5.string().max(190).optional().describe("Owner contact. Displayed verbatim; not used for auth.")
|
|
251
|
-
};
|
|
252
|
-
var gateMetadataPatchSchema = z5.object(gateMetadataFields);
|
|
253
|
-
var gateCreateSchema = z5.object({
|
|
254
|
-
name: gateNameSchema,
|
|
255
|
-
enabled: z5.boolean().default(true).describe(
|
|
256
|
-
"Master switch. Defaults to `true`. Set `false` to create the gate disabled (evaluates to `false` regardless of rules/rollout); flip on via `POST /{id}/enable` or PATCH."
|
|
257
|
-
),
|
|
258
|
-
rollout_pct: z5.number().int().min(0).max(1e4).default(0).describe(
|
|
259
|
-
"Initial rollout in **basis points** (0\u201310000 = 0%\u2013100%) \u2014 `100` here means **1%**, not 100%. Use `rollout_percent` (0\u2013100) below if you'd rather think in percent. Use `0` to create the gate dark and ramp via PATCH after deploy validation."
|
|
260
|
-
),
|
|
261
|
-
rollout_percent: z5.number().min(0).max(100).optional().describe(
|
|
262
|
-
"Initial rollout as a **percentage** (0\u2013100, fractional ok). Friendlier alias for `rollout_pct`; converted internally to basis points (e.g. `100` here = 10000 bp = 100%). If both `rollout_pct` and `rollout_percent` are set, `rollout_percent` wins."
|
|
263
|
-
),
|
|
264
|
-
rules: z5.array(gateRuleSchema).default([]).describe(
|
|
265
|
-
"Targeting predicates. AND-combined. If non-empty, the gate returns `true` only for callers that satisfy every rule **and** fall under `rollout_pct`."
|
|
266
|
-
),
|
|
267
|
-
salt: z5.string().min(1).max(64).optional().describe(
|
|
268
|
-
"Hash salt for percentage bucketing. Auto-generated if omitted. Provide explicitly to keep a gate's buckets stable across delete/recreate. **Immutable after create** \u2014 there is no PATCH for `salt` because changing it would re-bucket every caller."
|
|
269
|
-
),
|
|
270
|
-
stack: z5.array(stackedGateEntrySchema).nullable().optional().describe(
|
|
271
|
-
"Optional gatekeeper stack. When provided, takes precedence over `rules` + `rollout_pct` at evaluation time. Omit (or pass `null`) for a flat gate."
|
|
272
|
-
),
|
|
273
|
-
...gateMetadataFields
|
|
274
|
-
}).describe(
|
|
275
|
-
"Body for `POST /api/admin/gates`. At minimum supply `name`; everything else has sensible defaults."
|
|
276
|
-
);
|
|
277
|
-
var gateUpdateSchema = z5.object({
|
|
278
|
-
rollout_pct: z5.number().int().min(0).max(1e4).optional().describe(
|
|
279
|
-
"New rollout in **basis points** (0\u201310000 = 0%\u2013100%) \u2014 `100` here means **1%**. Use `rollout_percent` (0\u2013100) below for percent. Omit both to leave unchanged."
|
|
280
|
-
),
|
|
281
|
-
rollout_percent: z5.number().min(0).max(100).optional().describe(
|
|
282
|
-
"New rollout as a **percentage** (0\u2013100). Friendlier alias for `rollout_pct`; converted internally. Wins over `rollout_pct` if both are supplied. Omit both to leave unchanged."
|
|
283
|
-
),
|
|
284
|
-
rules: z5.array(gateRuleSchema).optional().describe(
|
|
285
|
-
"Replaces the rule list wholesale. To add a value to an `in` rule, send the full new `rules` array with the augmented `value` (e.g. previous `['US','CA']` \u2192 `['US','CA','GB']`)."
|
|
286
|
-
),
|
|
287
|
-
enabled: z5.boolean().optional().describe(
|
|
288
|
-
"Master switch. `false` makes the gate evaluate to `false` for every caller regardless of `rollout_pct`, `rules`, or `stack` \u2014 use as kill switch."
|
|
289
|
-
),
|
|
290
|
-
stack: z5.array(stackedGateEntrySchema).nullable().optional().describe(
|
|
291
|
-
"Replaces the gatekeeper stack wholesale. Send `null` to revert to flat `rules` + `rollout_pct` evaluation."
|
|
292
|
-
),
|
|
293
|
-
...gateMetadataFields
|
|
294
|
-
}).describe(
|
|
295
|
-
"Body for `PATCH /api/admin/gates/{id}`. Partial \u2014 only supplied fields change. Array fields (`rules`, `stack`) replace, not merge."
|
|
296
|
-
);
|
|
297
|
-
var gateResponseSchema = z5.object({
|
|
298
|
-
id: z5.string().describe("Stable opaque gate id (`gat_\u2026`)."),
|
|
299
|
-
name: gateNameSchema,
|
|
300
|
-
/** D1 stores enabled as 0/1; clients see either form. */
|
|
301
|
-
enabled: z5.union([z5.boolean(), z5.number().int().min(0).max(1)]).describe("Master switch. Returned as `1`/`0` from D1; treat truthy as enabled."),
|
|
302
|
-
/** Rollout percentage in basis points (0–10000 = 0%–100%). */
|
|
303
|
-
rolloutPct: z5.number().int().min(0).max(1e4).describe("Current rollout in basis points (0\u201310000)."),
|
|
304
|
-
rules: z5.array(gateRuleSchema).optional(),
|
|
305
|
-
salt: z5.string().optional().describe("Hash salt used for percentage bucketing."),
|
|
306
|
-
title: z5.string().nullable().optional(),
|
|
307
|
-
description: z5.string().nullable().optional(),
|
|
308
|
-
folder: z5.string().nullable().optional(),
|
|
309
|
-
groupName: z5.string().nullable().optional().describe("Group label (note: `groupName` in response, `group` in request)."),
|
|
310
|
-
ownerEmail: z5.string().nullable().optional().describe("Owner email (note: `ownerEmail` in response, `owner_email` in request)."),
|
|
311
|
-
stack: z5.array(stackedGateEntrySchema).nullable().optional(),
|
|
312
|
-
updatedAt: z5.string().describe("ISO-8601 timestamp of last mutation.")
|
|
313
|
-
});
|
|
314
|
-
var gateCreateResponseSchema = z5.object({
|
|
315
|
-
id: z5.string().describe("Newly assigned gate id (`gat_\u2026`)."),
|
|
316
|
-
name: gateNameSchema
|
|
317
|
-
});
|
|
318
|
-
var gateUpdateResponseSchema = z5.object({ id: z5.string().describe("Gate id that was updated.") }).describe("Update returns only the id \u2014 re-fetch via `GET /api/admin/gates` for the full row.");
|
|
319
|
-
var gateDeleteResponseSchema = z5.object({ ok: z5.literal(true) });
|
|
320
|
-
var gateToggleResponseSchema = z5.object({
|
|
321
|
-
id: z5.string(),
|
|
322
|
-
enabled: z5.boolean()
|
|
323
|
-
});
|
|
324
|
-
|
|
325
|
-
// src/schemas/killswitches.ts
|
|
326
|
-
import { z as z6 } from "zod";
|
|
327
|
-
var SWITCH_KEY_RE = /^[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?$/;
|
|
328
|
-
var switchKeySchema = z6.string().max(64).regex(SWITCH_KEY_RE, {
|
|
329
|
-
message: "Switch keys must be lowercase, no dots."
|
|
330
|
-
}).describe(
|
|
331
|
-
"Single-segment switch key (lowercase letters, digits, `_`/`-`; no dots). Used as the nested switch entry inside a killswitch's `switches` map."
|
|
332
|
-
);
|
|
333
|
-
var killswitchValueSchema = z6.object({
|
|
334
|
-
value: z6.boolean().describe(
|
|
335
|
-
"Default boolean delivered when no `switches` entry matches the caller's switch key."
|
|
336
|
-
),
|
|
337
|
-
switches: z6.record(switchKeySchema, z6.boolean()).optional().describe(
|
|
338
|
-
"Per-switch overrides. When present, a matching key takes precedence over `value` on the client."
|
|
339
|
-
)
|
|
340
|
-
}).describe("Runtime killswitch payload returned to SDKs.");
|
|
341
|
-
var killswitchCreateSchema = z6.object({
|
|
342
|
-
name: configNameSchema.describe(
|
|
343
|
-
"Stable killswitch key in `folder.name` form (two lowercase segments separated by a dot \u2014 e.g. `payments.checkout`). Immutable after create."
|
|
344
|
-
),
|
|
345
|
-
description: z6.string().max(512).optional().describe("Optional free-form description shown in the dashboard. Max 512 chars."),
|
|
346
|
-
folder: folderSchema,
|
|
347
|
-
value: z6.boolean().optional().describe(
|
|
348
|
-
"Default value applied to every env at creation. Defaults to `false`. Use `true` to ship the killswitch pre-tripped."
|
|
349
|
-
),
|
|
350
|
-
switches: z6.record(switchKeySchema, z6.boolean()).optional().describe(
|
|
351
|
-
"Initial per-switch overrides applied to every env. Empty/omitted leaves the killswitch with only the flat `value`."
|
|
352
|
-
)
|
|
353
|
-
}).describe("Body for `POST /api/admin/killswitches`. Only `name` is required.");
|
|
354
|
-
var killswitchUpdateSchema = z6.object({
|
|
355
|
-
description: z6.string().max(512).nullish().describe("New description, or `null` to clear it. Max 512 chars."),
|
|
356
|
-
folder: folderSchema,
|
|
357
|
-
value: z6.boolean().optional().describe(
|
|
358
|
-
"Flat value applied to every env. Publishes a new version per env when set. Omit to leave values unchanged."
|
|
359
|
-
),
|
|
360
|
-
switches: z6.record(switchKeySchema, z6.boolean()).optional().describe(
|
|
361
|
-
"Replace the switches map wholesale on every env. To edit a single entry on a single env use `PUT /{id}/switch` instead."
|
|
362
|
-
)
|
|
363
|
-
}).describe(
|
|
364
|
-
"Body for `PATCH /api/admin/killswitches/{id}`. Partial \u2014 only supplied fields change. `switches` replaces wholesale (no merge)."
|
|
365
|
-
);
|
|
366
|
-
var killswitchSwitchSetSchema = z6.object({
|
|
367
|
-
env: configEnvSchema.describe("Target environment (`dev`/`stage`/`prod`)."),
|
|
368
|
-
switchKey: switchKeySchema.describe("Switch key to set."),
|
|
369
|
-
value: z6.boolean().describe("New boolean value for this `switchKey` on this `env`.")
|
|
370
|
-
}).describe(
|
|
371
|
-
"Body for `PUT /api/admin/killswitches/{id}/switch`. Sets or updates one switch entry on one env."
|
|
372
|
-
);
|
|
373
|
-
var killswitchValueSetSchema = z6.object({
|
|
374
|
-
env: configEnvSchema.describe("Target environment whose flat default flips."),
|
|
375
|
-
value: z6.boolean().describe("New flat default for this env. Existing switches carry forward unchanged.")
|
|
376
|
-
}).describe(
|
|
377
|
-
"Body for the killswitch quick-toggle. Sets the flat `value` on one env without touching its switches."
|
|
378
|
-
);
|
|
379
|
-
var killswitchSwitchUnsetSchema = z6.object({
|
|
380
|
-
env: configEnvSchema.describe("Target environment."),
|
|
381
|
-
switchKey: switchKeySchema.describe("Switch key to remove.")
|
|
382
|
-
}).describe(
|
|
383
|
-
"Body for `DELETE /api/admin/killswitches/{id}/switch`. Removes one switch entry from one env."
|
|
384
|
-
);
|
|
385
|
-
var killswitchEnvEntrySchema = z6.object({
|
|
386
|
-
value: z6.boolean().describe("Flat boolean published for this env."),
|
|
387
|
-
switches: z6.record(switchKeySchema, z6.boolean()).optional(),
|
|
388
|
-
version: z6.number().int().describe("Monotonically increasing version per env."),
|
|
389
|
-
publishedAt: z6.string().describe("ISO-8601 publish timestamp for this version.")
|
|
390
|
-
});
|
|
391
|
-
var killswitchResponseSchema = z6.object({
|
|
392
|
-
id: z6.string().describe("Stable opaque killswitch id."),
|
|
393
|
-
name: configNameSchema,
|
|
394
|
-
description: z6.string().nullable().describe("Free-form description or `null`."),
|
|
395
|
-
updatedAt: z6.string().describe("ISO-8601 timestamp of last mutation."),
|
|
396
|
-
envs: z6.record(configEnvSchema, killswitchEnvEntrySchema).describe("Per-env latest value, switches, version, and publish timestamp.")
|
|
397
|
-
});
|
|
398
|
-
var killswitchCreateResponseSchema = z6.object({
|
|
399
|
-
id: z6.string().describe("Newly assigned killswitch id."),
|
|
400
|
-
name: configNameSchema
|
|
401
|
-
});
|
|
402
|
-
var killswitchUpdateResponseSchema = z6.object({
|
|
403
|
-
id: z6.string().describe("Killswitch id that was updated.")
|
|
404
|
-
});
|
|
405
|
-
var killswitchDeleteResponseSchema = z6.object({ ok: z6.literal(true) });
|
|
406
|
-
var killswitchSwitchSetResponseSchema = z6.object({
|
|
407
|
-
id: z6.string(),
|
|
408
|
-
env: configEnvSchema,
|
|
409
|
-
switchKey: switchKeySchema,
|
|
410
|
-
value: z6.boolean()
|
|
411
|
-
});
|
|
412
|
-
var killswitchSwitchUnsetResponseSchema = z6.object({
|
|
413
|
-
id: z6.string(),
|
|
414
|
-
env: configEnvSchema,
|
|
415
|
-
switchKey: switchKeySchema,
|
|
416
|
-
removed: z6.boolean().describe("`true` if the entry existed and was removed, `false` if no-op.")
|
|
417
|
-
});
|
|
418
|
-
|
|
419
|
-
// src/schemas/universes.ts
|
|
420
|
-
import { z as z7 } from "zod";
|
|
421
|
-
var universeNameSchema = z7.string().regex(/^[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?)?$/).max(128).describe(
|
|
422
|
-
"Stable universe key. Single segment or `folder.name`. Lowercase letters, digits, `_` or `-`; max 128 chars. Immutable after create."
|
|
423
|
-
);
|
|
424
|
-
var holdoutRangeSchema = z7.tuple([z7.number().int().min(0).max(9999), z7.number().int().min(0).max(9999)]).refine(([lo, hi]) => lo <= hi, "lo must be <= hi").nullable().describe(
|
|
425
|
-
"Inclusive `[lo, hi]` bucket range (0\u20139999) reserved as the **holdout** \u2014 callers hashed into this slice are excluded from every experiment in the universe. `null` disables the holdout. Pro plan or higher required."
|
|
426
|
-
);
|
|
427
|
-
var universeCreateSchema = z7.object({
|
|
428
|
-
name: universeNameSchema,
|
|
429
|
-
folder: folderSchema,
|
|
430
|
-
unit_type: z7.string().default("user_id").describe(
|
|
431
|
-
"Unit of randomisation. Typically `user_id`. Use `account_id` to keep whole accounts in the same group across an experiment."
|
|
432
|
-
),
|
|
433
|
-
holdout_range: holdoutRangeSchema.default(null)
|
|
434
|
-
}).describe("Body for `POST /api/admin/universes`. Only `name` is required.");
|
|
435
|
-
var universeUpdateSchema = z7.object({
|
|
436
|
-
folder: folderSchema,
|
|
437
|
-
holdout_range: holdoutRangeSchema.optional()
|
|
438
|
-
}).describe(
|
|
439
|
-
"Body for `PATCH /api/admin/universes/{id}`. Only `holdout_range` is mutable \u2014 name and unit_type are immutable after create."
|
|
440
|
-
);
|
|
441
|
-
var universeResponseSchema = z7.object({
|
|
442
|
-
id: z7.string().describe("Stable opaque universe id."),
|
|
443
|
-
name: universeNameSchema,
|
|
444
|
-
unitType: z7.string().describe("Unit of randomisation. Snake-case in request (`unit_type`), camelCase in response."),
|
|
445
|
-
holdoutRange: z7.tuple([z7.number(), z7.number()]).nullable().describe("Reserved holdout bucket range, or `null` if none."),
|
|
446
|
-
createdAt: z7.string().describe("ISO-8601 timestamp of creation.")
|
|
447
|
-
});
|
|
448
|
-
var universeCreateResponseSchema = z7.object({
|
|
449
|
-
id: z7.string().describe("Newly assigned universe id."),
|
|
450
|
-
name: universeNameSchema
|
|
451
|
-
});
|
|
452
|
-
var universeUpdateResponseSchema = z7.object({
|
|
453
|
-
id: z7.string().describe("Universe id that was updated.")
|
|
454
|
-
});
|
|
455
|
-
var universeDeleteResponseSchema = z7.object({ ok: z7.literal(true) });
|
|
456
|
-
|
|
457
|
-
// src/schemas/experiments.ts
|
|
458
|
-
import { z as z8 } from "zod";
|
|
459
|
-
var experimentInlineMetricSchema = z8.object({
|
|
460
|
-
name: metricNameSchema.optional(),
|
|
461
|
-
query: z8.string().min(1).max(4096).describe("Metric DSL string, e.g. `count_users(checkout_completed)`.")
|
|
462
|
-
});
|
|
463
|
-
var experimentNameSchema = z8.string().regex(/^[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?)?$/).max(128).describe(
|
|
464
|
-
"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."
|
|
465
|
-
);
|
|
466
|
-
var experimentGroupSchema = z8.object({
|
|
467
|
-
name: z8.string().min(1).max(64).describe("Group label (e.g. `control`, `treatment_a`). Max 64 chars."),
|
|
468
|
-
weight: z8.number().int().min(0).max(1e4).describe(
|
|
469
|
-
"Allocation weight in basis points (0\u201310000). The sum across all groups must equal exactly **10000** (100%)."
|
|
470
|
-
),
|
|
471
|
-
params: z8.record(z8.string(), z8.unknown()).default({}).describe(
|
|
472
|
-
"Per-group parameter values delivered to the SDK when a caller is hashed into this group. Keys must match the experiment's `params` schema."
|
|
473
|
-
)
|
|
474
|
-
}).describe("One variant in the experiment. Weights across all groups must sum to 10000.");
|
|
475
|
-
var paramSchemaSchema = z8.record(z8.string(), z8.enum(["string", "bool", "number"])).describe(
|
|
476
|
-
"Map of param-name \u2192 scalar type. Defines the shape of `groups[].params`. Example: `{ headline: 'string', show_cta: 'bool' }`."
|
|
477
|
-
);
|
|
478
|
-
var bucketBySchema = z8.string().min(1).max(128).regex(
|
|
479
|
-
/^[a-zA-Z_][a-zA-Z0-9_]*$/,
|
|
480
|
-
"Must be a user-attribute name (letters, digits, underscore; not starting with a digit)."
|
|
481
|
-
).describe(
|
|
482
|
-
"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."
|
|
483
|
-
);
|
|
484
|
-
var experimentCreateSchema = z8.object({
|
|
485
|
-
name: experimentNameSchema,
|
|
486
|
-
description: z8.string().max(2e3).nullable().default(null).describe("Free-form description. Max 2000 chars, markdown rendered in the dashboard."),
|
|
487
|
-
hypothesis: z8.string().max(4e3).nullable().default(null).describe("Hypothesis statement shown in the editor. Display-only."),
|
|
488
|
-
tag: z8.string().max(64).nullable().default(null).describe("Short tag chip rendered next to the name. Display-only."),
|
|
489
|
-
owner_email: z8.string().max(254).nullable().default(null).describe("Owner email. Display-only."),
|
|
490
|
-
audience: z8.string().max(256).nullable().default(null).describe("Audience label shown in the editor. Display-only."),
|
|
491
|
-
bucket_by: bucketBySchema.nullable().default(null),
|
|
492
|
-
folder: folderSchema,
|
|
493
|
-
universe: z8.string().min(1).describe(
|
|
494
|
-
"Name of an existing universe in the project. Returns `422` if the universe doesn't exist."
|
|
495
|
-
),
|
|
496
|
-
targeting_gate: z8.string().nullable().default(null).describe(
|
|
497
|
-
"Optional gate name. Only callers that pass the gate are enrolled in the experiment."
|
|
498
|
-
),
|
|
499
|
-
allocation_pct: z8.number().int().min(0).max(1e4).default(0).describe(
|
|
500
|
-
"Share of the (gated) audience allocated to the experiment, in basis points (0\u201310000 = 0%\u2013100%). `0` = unallocated. Immutable while the experiment is running."
|
|
501
|
-
),
|
|
502
|
-
salt: z8.string().min(1).max(64).optional().describe("Hash salt for bucketing. Auto-generated if omitted. Immutable while running."),
|
|
503
|
-
params: paramSchemaSchema.default({}),
|
|
504
|
-
groups: z8.array(experimentGroupSchema).min(2).describe(
|
|
505
|
-
"Two or more variants. Weights must sum to exactly 10000 (100%). Immutable while running."
|
|
506
|
-
),
|
|
507
|
-
significance_threshold: z8.number().min(1e-4).max(0.5).default(0.05).describe(
|
|
508
|
-
"p-value cutoff used by the analysis pass. Defaults to `0.05`. Values other than 0.05 require Pro plan or higher."
|
|
509
|
-
),
|
|
510
|
-
min_runtime_days: z8.number().int().min(0).max(365).default(0).describe("Minimum days the experiment must run before results are considered conclusive."),
|
|
511
|
-
min_sample_size: z8.number().int().min(1).default(100).describe("Minimum exposures per group before results are considered conclusive."),
|
|
512
|
-
sequential_testing: z8.boolean().default(false).describe(
|
|
513
|
-
"Enable sequential testing (always-valid p-values). Requires Premium plan or higher."
|
|
514
|
-
),
|
|
515
|
-
goal_metric: experimentInlineMetricSchema.optional().describe(
|
|
516
|
-
"Single goal metric defined inline as a DSL query. The underlying event is auto-created if missing."
|
|
517
|
-
),
|
|
518
|
-
guardrail_metrics: z8.array(experimentInlineMetricSchema).max(10).default([]).describe(
|
|
519
|
-
"Up to 10 guardrail metrics defined inline. Each is upserted (event + metric) and attached with role=guardrail."
|
|
520
|
-
)
|
|
521
|
-
}).refine(
|
|
522
|
-
(e) => e.groups.reduce((s, g) => s + g.weight, 0) === 1e4,
|
|
523
|
-
"groups weights must sum to exactly 10000"
|
|
524
|
-
).describe(
|
|
525
|
-
"Body for `POST /api/admin/experiments`. `name`, `universe`, and `groups` (\u22652, weights sum to 10000) required."
|
|
526
|
-
);
|
|
527
|
-
var experimentUpdateSchema = z8.object({
|
|
528
|
-
name: experimentNameSchema.optional(),
|
|
529
|
-
description: z8.string().max(2e3).nullable().optional(),
|
|
530
|
-
hypothesis: z8.string().max(4e3).nullable().optional(),
|
|
531
|
-
tag: z8.string().max(64).nullable().optional(),
|
|
532
|
-
owner_email: z8.string().max(254).nullable().optional(),
|
|
533
|
-
audience: z8.string().max(256).nullable().optional(),
|
|
534
|
-
bucket_by: bucketBySchema.nullable().optional(),
|
|
535
|
-
folder: folderSchema,
|
|
536
|
-
targeting_gate: z8.string().nullable().optional(),
|
|
537
|
-
allocation_pct: z8.number().int().min(0).max(1e4).optional().describe("Basis-points allocation (0\u201310000). Immutable while the experiment is running."),
|
|
538
|
-
salt: z8.string().min(1).max(64).optional().describe("Hash salt. Immutable while running."),
|
|
539
|
-
universe: z8.string().optional().describe(
|
|
540
|
-
"New universe name. Immutable while running. Returns `422` if the universe doesn't exist."
|
|
541
|
-
),
|
|
542
|
-
params: paramSchemaSchema.optional(),
|
|
543
|
-
groups: z8.array(experimentGroupSchema).min(2).optional().describe("Replacement groups. Weights must sum to 10000. Immutable while running."),
|
|
544
|
-
significance_threshold: z8.number().min(1e-4).max(0.5).optional(),
|
|
545
|
-
min_runtime_days: z8.number().int().min(0).max(365).optional(),
|
|
546
|
-
min_sample_size: z8.number().int().min(1).optional(),
|
|
547
|
-
sequential_testing: z8.boolean().optional(),
|
|
548
|
-
goal_metric: experimentInlineMetricSchema.optional().describe("Replaces the goal metric (event auto-upserted)."),
|
|
549
|
-
guardrail_metrics: z8.array(experimentInlineMetricSchema).max(10).optional().describe("Replaces the guardrail set wholesale (event auto-upserted per entry).")
|
|
550
|
-
}).describe(
|
|
551
|
-
"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)."
|
|
552
|
-
);
|
|
553
|
-
var experimentStatusUpdateSchema = z8.object({
|
|
554
|
-
status: z8.enum(["draft", "running", "stopped", "archived"]).describe(
|
|
555
|
-
"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."
|
|
556
|
-
)
|
|
557
|
-
}).describe("Body for `POST /api/admin/experiments/{id}/status`.");
|
|
558
|
-
var experimentMetricsUpdateSchema = z8.object({
|
|
559
|
-
metrics: z8.array(
|
|
560
|
-
z8.object({
|
|
561
|
-
metric_id: z8.string().describe("Existing metric id in the project."),
|
|
562
|
-
role: z8.enum(["goal", "guardrail", "secondary"]).describe(
|
|
563
|
-
"Metric role. `goal` drives the decision, `guardrail` blocks ship if degraded, `secondary` is informational."
|
|
564
|
-
)
|
|
565
|
-
})
|
|
566
|
-
).describe("Replacement metrics list \u2014 replaces the current attachments wholesale.")
|
|
567
|
-
}).describe(
|
|
568
|
-
"Body for `POST /api/admin/experiments/{id}/metrics`. Replaces the experiment's metric attachments wholesale. Returns `422` if any `metric_id` is unknown."
|
|
569
|
-
);
|
|
570
|
-
var experimentResponseSchema = z8.object({
|
|
571
|
-
id: z8.string().describe("Stable opaque experiment id (`exp_\u2026`)."),
|
|
572
|
-
name: experimentNameSchema,
|
|
573
|
-
description: z8.string().nullable(),
|
|
574
|
-
hypothesis: z8.string().nullable(),
|
|
575
|
-
tag: z8.string().nullable(),
|
|
576
|
-
ownerEmail: z8.string().nullable(),
|
|
577
|
-
audience: z8.string().nullable(),
|
|
578
|
-
bucketBy: bucketBySchema.nullable(),
|
|
579
|
-
folder: z8.string().nullable(),
|
|
580
|
-
status: z8.enum(["draft", "running", "stopped", "archived"]),
|
|
581
|
-
universe: z8.string().describe("Universe name this experiment draws from."),
|
|
582
|
-
targetingGate: z8.string().nullable(),
|
|
583
|
-
allocationPct: z8.number().int().describe("Allocation in basis points (0\u201310000)."),
|
|
584
|
-
salt: z8.string(),
|
|
585
|
-
params: z8.record(z8.string(), z8.enum(["string", "bool", "number"])),
|
|
586
|
-
groups: z8.array(experimentGroupSchema),
|
|
587
|
-
significanceThreshold: z8.number(),
|
|
588
|
-
minRuntimeDays: z8.number().int(),
|
|
589
|
-
minSampleSize: z8.number().int(),
|
|
590
|
-
sequentialTesting: z8.boolean(),
|
|
591
|
-
startedAt: z8.string().nullable().describe("ISO-8601 timestamp the experiment last transitioned to `running`, or `null`."),
|
|
592
|
-
stoppedAt: z8.string().nullable().optional(),
|
|
593
|
-
updatedAt: z8.string()
|
|
594
|
-
});
|
|
595
|
-
var experimentCreateResponseSchema = z8.object({
|
|
596
|
-
id: z8.string().describe("Newly assigned experiment id."),
|
|
597
|
-
name: experimentNameSchema
|
|
598
|
-
});
|
|
599
|
-
var experimentUpdateResponseSchema = z8.object({
|
|
600
|
-
id: z8.string().describe("Experiment id that was updated.")
|
|
601
|
-
});
|
|
602
|
-
var experimentDeleteResponseSchema = z8.object({ ok: z8.literal(true) });
|
|
603
|
-
var experimentStatusResponseSchema = z8.object({
|
|
604
|
-
id: z8.string(),
|
|
605
|
-
status: z8.enum(["draft", "running", "stopped", "archived"])
|
|
606
|
-
});
|
|
607
|
-
var experimentMetricsResponseSchema = z8.object({
|
|
608
|
-
id: z8.string(),
|
|
609
|
-
metrics: z8.array(
|
|
610
|
-
z8.object({
|
|
611
|
-
metric_id: z8.string(),
|
|
612
|
-
role: z8.enum(["goal", "guardrail", "secondary"])
|
|
613
|
-
})
|
|
614
|
-
)
|
|
615
|
-
});
|
|
616
|
-
var experimentResultRowSchema = z8.object({
|
|
617
|
-
metric: z8.string(),
|
|
618
|
-
group_name: z8.string(),
|
|
619
|
-
ds: z8.string().describe("Date slice (`YYYY-MM-DD`)."),
|
|
620
|
-
n: z8.number().nullable(),
|
|
621
|
-
mean: z8.number().nullable(),
|
|
622
|
-
delta_pct: z8.number().nullable(),
|
|
623
|
-
p_value: z8.number().nullable(),
|
|
624
|
-
srm_detected: z8.number().nullable().describe("`1` if sample-ratio mismatch detected, else `0`.")
|
|
625
|
-
});
|
|
626
|
-
var experimentResultsResponseSchema = z8.object({
|
|
627
|
-
experiment: z8.object({
|
|
628
|
-
id: z8.string(),
|
|
629
|
-
name: experimentNameSchema,
|
|
630
|
-
status: z8.enum(["draft", "running", "stopped", "archived"])
|
|
631
|
-
}),
|
|
632
|
-
results: z8.array(experimentResultRowSchema)
|
|
633
|
-
});
|
|
634
|
-
var experimentTimeseriesResponseSchema = z8.object({
|
|
635
|
-
experiment: z8.object({
|
|
636
|
-
id: z8.string(),
|
|
637
|
-
name: experimentNameSchema,
|
|
638
|
-
status: z8.enum(["draft", "running", "stopped", "archived"])
|
|
639
|
-
}),
|
|
640
|
-
series: z8.array(experimentResultRowSchema)
|
|
641
|
-
});
|
|
642
|
-
var experimentReanalyzeResponseSchema = z8.object({
|
|
643
|
-
id: z8.string(),
|
|
644
|
-
queued: z8.literal(true)
|
|
645
|
-
});
|
|
646
|
-
|
|
647
|
-
// src/schemas/alert-rules.ts
|
|
648
|
-
import { z as z9 } from "zod";
|
|
649
|
-
var ALERT_COMPARATORS = ["gt", "gte", "lt", "lte"];
|
|
650
|
-
var ALERT_SEVERITIES = ["danger", "warn", "info"];
|
|
651
|
-
var notificationTargetSchema = z9.object({
|
|
652
|
-
slackChannel: z9.object({
|
|
653
|
-
id: z9.string().min(1).describe("Slack channel id (e.g. C0123ABCD)."),
|
|
654
|
-
name: z9.string().min(1).describe("Slack channel name (without the leading #).")
|
|
655
|
-
}).nullish().describe("Slack channel to post this rule's alert to. Requires a Slack connector."),
|
|
656
|
-
email: z9.string().email().nullish().describe("Email address to notify for this rule (overrides the default recipient).")
|
|
657
|
-
}).strict();
|
|
658
|
-
var alertRuleCreateSchema = z9.object({
|
|
659
|
-
name: z9.string().min(1).max(120).describe("Human label for the rule, shown on the alert and the rules list."),
|
|
660
|
-
metricId: z9.string().min(1).describe("Id of the metric to evaluate."),
|
|
661
|
-
comparator: z9.enum(ALERT_COMPARATORS).describe("How the metric value is compared to the threshold (gt/gte/lt/lte)."),
|
|
662
|
-
threshold: z9.number().finite().describe("Threshold the metric value is compared against."),
|
|
663
|
-
windowHours: z9.number().int().min(1).max(720).default(24).describe("Lookback window (hours) the metric is aggregated over. 1\u2013720."),
|
|
664
|
-
severity: z9.enum(ALERT_SEVERITIES).default("warn").describe("Severity of the raised alert."),
|
|
665
|
-
enabled: z9.boolean().default(true).describe("Whether the rule is evaluated by the cron."),
|
|
666
|
-
notify: notificationTargetSchema.nullish().describe("Where to deliver this rule's alert (Slack channel and/or email target).")
|
|
667
|
-
}).strict();
|
|
668
|
-
var alertRuleUpdateSchema = z9.object({
|
|
669
|
-
name: z9.string().min(1).max(120),
|
|
670
|
-
comparator: z9.enum(ALERT_COMPARATORS),
|
|
671
|
-
threshold: z9.number().finite(),
|
|
672
|
-
windowHours: z9.number().int().min(1).max(720),
|
|
673
|
-
severity: z9.enum(ALERT_SEVERITIES),
|
|
674
|
-
enabled: z9.boolean(),
|
|
675
|
-
notify: notificationTargetSchema.nullish()
|
|
676
|
-
}).partial().strict();
|
|
677
|
-
export {
|
|
678
|
-
ALERT_COMPARATORS,
|
|
679
|
-
ALERT_SEVERITIES,
|
|
680
|
-
CONFIG_ENVS,
|
|
681
|
-
CONFIG_KINDS,
|
|
682
|
-
DEFAULT_PAGE_SIZE,
|
|
683
|
-
MAX_PAGE_SIZE,
|
|
684
|
-
SWITCH_KEY_RE,
|
|
685
|
-
alertRuleCreateSchema,
|
|
686
|
-
alertRuleUpdateSchema,
|
|
687
|
-
bucketBySchema,
|
|
688
|
-
configActivityEntrySchema,
|
|
689
|
-
configActivityResponseSchema,
|
|
690
|
-
configCreateResponseSchema,
|
|
691
|
-
configCreateSchema,
|
|
692
|
-
configDeleteResponseSchema,
|
|
693
|
-
configDraftDiscardResponseSchema,
|
|
694
|
-
configDraftSaveResponseSchema,
|
|
695
|
-
configDraftUpsertSchema,
|
|
696
|
-
configEnvSchema,
|
|
697
|
-
configKindSchema,
|
|
698
|
-
configNameSchema,
|
|
699
|
-
configPublishResponseSchema,
|
|
700
|
-
configPublishSchema,
|
|
701
|
-
configResponseSchema,
|
|
702
|
-
configSchemaUpdateSchema,
|
|
703
|
-
configUpdateResponseSchema,
|
|
704
|
-
configUpdateSchema,
|
|
705
|
-
decodeCursor,
|
|
706
|
-
encodeCursor,
|
|
707
|
-
experimentCreateResponseSchema,
|
|
708
|
-
experimentCreateSchema,
|
|
709
|
-
experimentDeleteResponseSchema,
|
|
710
|
-
experimentGroupSchema,
|
|
711
|
-
experimentInlineMetricSchema,
|
|
712
|
-
experimentMetricsResponseSchema,
|
|
713
|
-
experimentMetricsUpdateSchema,
|
|
714
|
-
experimentNameSchema,
|
|
715
|
-
experimentReanalyzeResponseSchema,
|
|
716
|
-
experimentResponseSchema,
|
|
717
|
-
experimentResultsResponseSchema,
|
|
718
|
-
experimentStatusResponseSchema,
|
|
719
|
-
experimentStatusUpdateSchema,
|
|
720
|
-
experimentTimeseriesResponseSchema,
|
|
721
|
-
experimentUpdateResponseSchema,
|
|
722
|
-
experimentUpdateSchema,
|
|
723
|
-
folderSchema,
|
|
724
|
-
gateCreateResponseSchema,
|
|
725
|
-
gateCreateSchema,
|
|
726
|
-
gateDeleteResponseSchema,
|
|
727
|
-
gateMetadataPatchSchema,
|
|
728
|
-
gateNameSchema,
|
|
729
|
-
gateResponseSchema,
|
|
730
|
-
gateRuleSchema,
|
|
731
|
-
gateToggleResponseSchema,
|
|
732
|
-
gateUpdateResponseSchema,
|
|
733
|
-
gateUpdateSchema,
|
|
734
|
-
holdoutRangeSchema,
|
|
735
|
-
jsonSchemaSchema,
|
|
736
|
-
killswitchCreateResponseSchema,
|
|
737
|
-
killswitchCreateSchema,
|
|
738
|
-
killswitchDeleteResponseSchema,
|
|
739
|
-
killswitchResponseSchema,
|
|
740
|
-
killswitchSwitchSetResponseSchema,
|
|
741
|
-
killswitchSwitchSetSchema,
|
|
742
|
-
killswitchSwitchUnsetResponseSchema,
|
|
743
|
-
killswitchSwitchUnsetSchema,
|
|
744
|
-
killswitchUpdateResponseSchema,
|
|
745
|
-
killswitchUpdateSchema,
|
|
746
|
-
killswitchValueSchema,
|
|
747
|
-
killswitchValueSetSchema,
|
|
748
|
-
metricNameSchema,
|
|
749
|
-
notificationTargetSchema,
|
|
750
|
-
pageQuerySchema,
|
|
751
|
-
paramSchemaSchema,
|
|
752
|
-
rampSchema,
|
|
753
|
-
stackedGateEntrySchema,
|
|
754
|
-
switchKeySchema,
|
|
755
|
-
universeCreateResponseSchema,
|
|
756
|
-
universeCreateSchema,
|
|
757
|
-
universeDeleteResponseSchema,
|
|
758
|
-
universeNameSchema,
|
|
759
|
-
universeResponseSchema,
|
|
760
|
-
universeUpdateResponseSchema,
|
|
761
|
-
universeUpdateSchema
|
|
762
|
-
};
|