@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.
Files changed (151) hide show
  1. package/README.md +37 -0
  2. package/dist/chunk-643YSMLU.js +1616 -0
  3. package/dist/chunk-643YSMLU.js.map +1 -0
  4. package/dist/chunk-MRW3DJ57.js +3385 -0
  5. package/dist/chunk-MRW3DJ57.js.map +1 -0
  6. package/dist/client-K0ShmsC7.d.ts +11284 -0
  7. package/dist/client.d.ts +1 -0
  8. package/dist/client.js +215 -0
  9. package/dist/client.js.map +1 -0
  10. package/dist/custom.d.ts +86 -0
  11. package/dist/custom.js +300 -0
  12. package/dist/custom.js.map +1 -0
  13. package/dist/index.d.ts +29 -41
  14. package/dist/index.js +258 -4375
  15. package/dist/index.js.map +1 -0
  16. package/dist/node-context.d.ts +81 -33
  17. package/dist/node-context.js +98 -44
  18. package/dist/node-context.js.map +1 -0
  19. package/dist/schemas.d.ts +7973 -0
  20. package/dist/schemas.js +1099 -0
  21. package/dist/schemas.js.map +1 -0
  22. package/openapi.json +15922 -3232
  23. package/openapi.yaml +14277 -0
  24. package/package.json +33 -26
  25. package/spec/components/errors.yaml +53 -0
  26. package/spec/components/parameters.yaml +32 -0
  27. package/spec/components/responses.yaml +122 -0
  28. package/spec/components/schemas/alert-rules.yaml +175 -0
  29. package/spec/components/schemas/attributes.yaml +157 -0
  30. package/spec/components/schemas/common.yaml +92 -0
  31. package/spec/components/schemas/configs.yaml +371 -0
  32. package/spec/components/schemas/connectors.yaml +587 -0
  33. package/spec/components/schemas/errors-api.yaml +209 -0
  34. package/spec/components/schemas/events.yaml +321 -0
  35. package/spec/components/schemas/experiments.yaml +865 -0
  36. package/spec/components/schemas/gate-templates.yaml +237 -0
  37. package/spec/components/schemas/gates.yaml +890 -0
  38. package/spec/components/schemas/i18n.yaml +478 -0
  39. package/spec/components/schemas/keys.yaml +186 -0
  40. package/spec/components/schemas/killswitches.yaml +309 -0
  41. package/spec/components/schemas/metrics.yaml +547 -0
  42. package/spec/components/schemas/ops.yaml +1013 -0
  43. package/spec/components/schemas/projects.yaml +222 -0
  44. package/spec/components/schemas/search.yaml +46 -0
  45. package/spec/components/schemas/universes.yaml +127 -0
  46. package/spec/openapi.yaml +367 -0
  47. package/spec/paths/alert-rules.yaml +258 -0
  48. package/spec/paths/attributes.yaml +272 -0
  49. package/spec/paths/configs.yaml +758 -0
  50. package/spec/paths/connectors.yaml +782 -0
  51. package/spec/paths/errors.yaml +382 -0
  52. package/spec/paths/events.yaml +380 -0
  53. package/spec/paths/experiments.yaml +803 -0
  54. package/spec/paths/gate-templates.yaml +267 -0
  55. package/spec/paths/gates.yaml +465 -0
  56. package/spec/paths/i18n.yaml +557 -0
  57. package/spec/paths/keys.yaml +182 -0
  58. package/spec/paths/killswitches.yaml +574 -0
  59. package/spec/paths/metrics.yaml +313 -0
  60. package/spec/paths/ops.yaml +446 -0
  61. package/spec/paths/projects.yaml +210 -0
  62. package/spec/paths/search.yaml +62 -0
  63. package/spec/paths/universes.yaml +255 -0
  64. package/dist/operations/attributes.d.ts +0 -33
  65. package/dist/operations/attributes.js +0 -16
  66. package/dist/operations/cli-adapter.d.ts +0 -35
  67. package/dist/operations/cli-adapter.js +0 -91
  68. package/dist/operations/coerce.d.ts +0 -32
  69. package/dist/operations/coerce.js +0 -53
  70. package/dist/operations/configs.d.ts +0 -28
  71. package/dist/operations/configs.js +0 -119
  72. package/dist/operations/docs.d.ts +0 -37
  73. package/dist/operations/docs.js +0 -39
  74. package/dist/operations/events.d.ts +0 -28
  75. package/dist/operations/events.js +0 -121
  76. package/dist/operations/experiments.d.ts +0 -28
  77. package/dist/operations/experiments.js +0 -280
  78. package/dist/operations/gates.d.ts +0 -28
  79. package/dist/operations/gates.js +0 -132
  80. package/dist/operations/i18n.d.ts +0 -28
  81. package/dist/operations/i18n.js +0 -121
  82. package/dist/operations/index.d.ts +0 -63
  83. package/dist/operations/index.js +0 -1751
  84. package/dist/operations/killswitches.d.ts +0 -28
  85. package/dist/operations/killswitches.js +0 -126
  86. package/dist/operations/mcp-adapter.d.ts +0 -35
  87. package/dist/operations/mcp-adapter.js +0 -94
  88. package/dist/operations/metrics.d.ts +0 -30
  89. package/dist/operations/metrics.js +0 -114
  90. package/dist/operations/ops.d.ts +0 -28
  91. package/dist/operations/ops.js +0 -313
  92. package/dist/operations/projects.d.ts +0 -28
  93. package/dist/operations/projects.js +0 -36
  94. package/dist/operations/sdk-docs.d.ts +0 -28
  95. package/dist/operations/sdk-docs.js +0 -160
  96. package/dist/operations/types.d.ts +0 -138
  97. package/dist/operations/types.js +0 -9
  98. package/dist/operations/universes.d.ts +0 -28
  99. package/dist/operations/universes.js +0 -93
  100. package/dist/operations/util.d.ts +0 -33
  101. package/dist/operations/util.js +0 -9
  102. package/dist/resources/alert-rules.d.ts +0 -50
  103. package/dist/resources/alert-rules.js +0 -75
  104. package/dist/resources/attributes.d.ts +0 -19
  105. package/dist/resources/attributes.js +0 -10
  106. package/dist/resources/configs.d.ts +0 -681
  107. package/dist/resources/configs.js +0 -509
  108. package/dist/resources/events.d.ts +0 -61
  109. package/dist/resources/events.js +0 -42
  110. package/dist/resources/experiments.d.ts +0 -849
  111. package/dist/resources/experiments.js +0 -707
  112. package/dist/resources/gates.d.ts +0 -808
  113. package/dist/resources/gates.js +0 -522
  114. package/dist/resources/i18n.d.ts +0 -84
  115. package/dist/resources/i18n.js +0 -60
  116. package/dist/resources/index.d.ts +0 -2767
  117. package/dist/resources/index.js +0 -2563
  118. package/dist/resources/killswitches.d.ts +0 -436
  119. package/dist/resources/killswitches.js +0 -514
  120. package/dist/resources/metrics.d.ts +0 -54
  121. package/dist/resources/metrics.js +0 -40
  122. package/dist/resources/ops.d.ts +0 -88
  123. package/dist/resources/ops.js +0 -36
  124. package/dist/resources/projects.d.ts +0 -54
  125. package/dist/resources/projects.js +0 -14
  126. package/dist/resources/universes.d.ts +0 -185
  127. package/dist/resources/universes.js +0 -234
  128. package/dist/schemas/alert-rules.d.ts +0 -65
  129. package/dist/schemas/alert-rules.js +0 -37
  130. package/dist/schemas/configs.d.ts +0 -154
  131. package/dist/schemas/configs.js +0 -134
  132. package/dist/schemas/constants.d.ts +0 -6
  133. package/dist/schemas/constants.js +0 -7
  134. package/dist/schemas/experiments.d.ts +0 -229
  135. package/dist/schemas/experiments.js +0 -221
  136. package/dist/schemas/folder.d.ts +0 -10
  137. package/dist/schemas/folder.js +0 -8
  138. package/dist/schemas/gates.d.ts +0 -386
  139. package/dist/schemas/gates.js +0 -190
  140. package/dist/schemas/index.d.ts +0 -11
  141. package/dist/schemas/index.js +0 -762
  142. package/dist/schemas/killswitches.d.ts +0 -110
  143. package/dist/schemas/killswitches.js +0 -226
  144. package/dist/schemas/metric-name.d.ts +0 -5
  145. package/dist/schemas/metric-name.js +0 -6
  146. package/dist/schemas/pagination.d.ts +0 -37
  147. package/dist/schemas/pagination.js +0 -44
  148. package/dist/schemas/universes.d.ts +0 -35
  149. package/dist/schemas/universes.js +0 -55
  150. package/dist/transport.d.ts +0 -30
  151. package/dist/transport.js +0 -44
@@ -1,119 +0,0 @@
1
- // src/operations/util.ts
2
- var str = (i, k) => i[k] === void 0 ? void 0 : i[k];
3
-
4
- // src/operations/configs.ts
5
- var GROUP = ["release", "configs"];
6
- var configOperations = [
7
- {
8
- group: GROUP,
9
- name: "list",
10
- mutates: false,
11
- summary: "List all configs",
12
- description: "Return every dynamic config (name, schema, last updated).",
13
- params: [],
14
- examples: [{ run: "shipeasy release configs list" }],
15
- run: (client) => client.configs.listAll()
16
- },
17
- {
18
- group: GROUP,
19
- name: "get",
20
- mutates: false,
21
- summary: "Show a config by name",
22
- description: "Resolve a config by name and return its full detail (schema + per-env values + drafts).",
23
- params: [{ name: "name", type: "string", description: "Config name.", required: true, positional: true }],
24
- examples: [{ run: "shipeasy release configs get pricing" }],
25
- run: async (client, i) => {
26
- const c = await client.configs.resolve(i.name);
27
- return client.configs.get(c.id);
28
- }
29
- },
30
- {
31
- group: GROUP,
32
- name: "create",
33
- mutates: true,
34
- summary: "Create a config",
35
- description: "Create a dynamic config. Configs are JSON objects validated against a JSON Schema (object-only).",
36
- params: [
37
- { name: "name", type: "string", description: "Config name.", required: true, positional: true },
38
- { name: "schema", type: "json", description: "JSON Schema (top-level type=object). Defaults to a permissive object schema." },
39
- { name: "value", type: "json", description: "Initial value as a JSON object. Defaults to {}." },
40
- { name: "description", type: "string", description: "Human-readable description." }
41
- ],
42
- examples: [
43
- { note: "Permissive schema, empty value", run: "shipeasy release configs create pricing" },
44
- { note: "Seed an initial value", run: `shipeasy release configs create pricing --value '{"free_trial_days":14}'` }
45
- ],
46
- run: (client, i) => client.configs.create({
47
- name: i.name,
48
- schema: i.schema ?? { type: "object", properties: {}, additionalProperties: true },
49
- value: i.value ?? {},
50
- ...i.description ? { description: i.description } : {}
51
- })
52
- },
53
- {
54
- group: GROUP,
55
- name: "update",
56
- mutates: true,
57
- summary: "Update a config's value (flat, all envs)",
58
- description: "Legacy flat value update across all envs. Use draft + publish for per-env staging.",
59
- params: [
60
- { name: "name", type: "string", description: "Config name.", required: true, positional: true },
61
- { name: "value", type: "json", description: "New value (JSON object).", required: true }
62
- ],
63
- examples: [{ note: "Overwrite the value (all envs)", run: `shipeasy release configs update pricing --value '{"free_trial_days":30}'` }],
64
- run: async (client, i) => {
65
- const c = await client.configs.resolve(i.name);
66
- return client.configs.update(c.id, { value: i.value });
67
- }
68
- },
69
- {
70
- group: GROUP,
71
- name: "draft",
72
- mutates: true,
73
- summary: "Save a draft value for one env",
74
- description: "Stage a draft value for the given env, to publish later.",
75
- params: [
76
- { name: "name", type: "string", description: "Config name.", required: true, positional: true },
77
- { name: "env", type: "string", description: "Target env (e.g. prod, staging, dev).", required: true },
78
- { name: "value", type: "json", description: "Draft value (JSON object).", required: true }
79
- ],
80
- examples: [{ note: "Stage a prod value", run: `shipeasy release configs draft pricing --env prod --value '{"free_trial_days":30}'` }],
81
- run: async (client, i) => {
82
- const c = await client.configs.resolve(i.name);
83
- return client.configs.saveDraft(c.id, { env: i.env, value: i.value });
84
- }
85
- },
86
- {
87
- group: GROUP,
88
- name: "publish",
89
- mutates: true,
90
- summary: "Publish the saved draft for one env",
91
- description: "Promote the staged draft for the given env to live.",
92
- params: [
93
- { name: "name", type: "string", description: "Config name.", required: true, positional: true },
94
- { name: "env", type: "string", description: "Target env to publish.", required: true }
95
- ],
96
- examples: [{ note: "Promote staged draft to prod", run: "shipeasy release configs publish pricing --env prod" }],
97
- run: async (client, i) => {
98
- const c = await client.configs.resolve(i.name);
99
- return client.configs.publish(c.id, { env: str(i, "env") });
100
- }
101
- },
102
- {
103
- group: GROUP,
104
- name: "archive",
105
- mutates: true,
106
- summary: "Archive a config",
107
- description: "Archive (soft-delete) a config by name.",
108
- params: [{ name: "name", type: "string", description: "Config name.", required: true, positional: true }],
109
- examples: [{ run: "shipeasy release configs archive pricing" }],
110
- run: async (client, i) => {
111
- const c = await client.configs.resolve(i.name);
112
- await client.configs.delete(c.id);
113
- return { ok: true, archived: i.name };
114
- }
115
- }
116
- ];
117
- export {
118
- configOperations
119
- };
@@ -1,37 +0,0 @@
1
- import { Operation } from './types.js';
2
- import '../resources/index.js';
3
- import 'zod/v4/core';
4
- import 'zod';
5
- import '../resources/attributes.js';
6
- import '../transport.js';
7
- import '../resources/i18n.js';
8
- import '../resources/projects.js';
9
- import '../resources/ops.js';
10
- import '../resources/events.js';
11
- import '../resources/metrics.js';
12
- import '../resources/alert-rules.js';
13
- import '../schemas/alert-rules.js';
14
- import '../resources/killswitches.js';
15
- import '../schemas/killswitches.js';
16
- import '../schemas/pagination.js';
17
- import '../resources/universes.js';
18
- import '../schemas/universes.js';
19
- import '../resources/configs.js';
20
- import '../schemas/configs.js';
21
- import '../resources/experiments.js';
22
- import '../schemas/experiments.js';
23
- import '../resources/gates.js';
24
- import '../schemas/gates.js';
25
-
26
- /**
27
- * Render the registry to Markdown — the "documentation comes along for free"
28
- * half of the proposal. Both `cli/scripts/gen-cli-docs.ts` and
29
- * `mcp/scripts/gen-mcp-docs.ts` can call this instead of re-describing every
30
- * command/tool by hand, so help text, tool descriptions, and the docs site all
31
- * derive from the same `summary`/`description`/`params`/`examples`.
32
- */
33
- declare function renderOperationsMarkdown(ops: Operation[], opts?: {
34
- title?: string;
35
- }): string;
36
-
37
- export { renderOperationsMarkdown };
@@ -1,39 +0,0 @@
1
- // src/operations/types.ts
2
- var opId = (op) => [...op.group, op.name].join(".");
3
- var opMcpName = (op) => [...op.group, op.name].join("_");
4
- var opCli = (op) => [...op.group, op.name].join(" ");
5
-
6
- // src/operations/docs.ts
7
- function renderOperationsMarkdown(ops, opts = {}) {
8
- const lines = [];
9
- if (opts.title) lines.push(`# ${opts.title}`, "");
10
- for (const op of ops) {
11
- lines.push(`## \`${opId(op)}\``, "");
12
- lines.push(op.summary, "");
13
- if (op.description && op.description !== op.summary) lines.push(op.description, "");
14
- lines.push(
15
- `- **CLI:** \`shipeasy ${opCli(op)}\` \xB7 **MCP tool:** \`${opMcpName(op)}\` \xB7 ${op.mutates ? "write (needs project binding)" : "read"}`,
16
- ""
17
- );
18
- if (op.params.length) {
19
- lines.push("| Param | Type | Required | Description |", "| --- | --- | --- | --- |");
20
- for (const p of op.params) {
21
- const kind = p.positional ? `${p.type} (positional)` : p.type;
22
- lines.push(`| \`${p.name}\` | ${kind} | ${p.required ? "yes" : "no"} | ${p.description} |`);
23
- }
24
- lines.push("");
25
- }
26
- if (op.examples?.length) {
27
- lines.push("**Examples**", "");
28
- for (const ex of op.examples) {
29
- if (ex.note) lines.push(`- ${ex.note}:`);
30
- lines.push("```sh", ex.run, "```");
31
- }
32
- lines.push("");
33
- }
34
- }
35
- return lines.join("\n");
36
- }
37
- export {
38
- renderOperationsMarkdown
39
- };
@@ -1,28 +0,0 @@
1
- import { Operation } from './types.js';
2
- import '../resources/index.js';
3
- import 'zod/v4/core';
4
- import 'zod';
5
- import '../resources/attributes.js';
6
- import '../transport.js';
7
- import '../resources/i18n.js';
8
- import '../resources/projects.js';
9
- import '../resources/ops.js';
10
- import '../resources/events.js';
11
- import '../resources/metrics.js';
12
- import '../resources/alert-rules.js';
13
- import '../schemas/alert-rules.js';
14
- import '../resources/killswitches.js';
15
- import '../schemas/killswitches.js';
16
- import '../schemas/pagination.js';
17
- import '../resources/universes.js';
18
- import '../schemas/universes.js';
19
- import '../resources/configs.js';
20
- import '../schemas/configs.js';
21
- import '../resources/experiments.js';
22
- import '../schemas/experiments.js';
23
- import '../resources/gates.js';
24
- import '../schemas/gates.js';
25
-
26
- declare const eventOperations: Operation[];
27
-
28
- export { eventOperations };
@@ -1,121 +0,0 @@
1
- // src/operations/util.ts
2
- var str = (i, k) => i[k] === void 0 ? void 0 : i[k];
3
- var bool = (i, k) => i[k] === void 0 ? void 0 : i[k];
4
-
5
- // src/operations/events.ts
6
- var GROUP = ["events"];
7
- var PROPS_DESC = `Event properties as a JSON array, e.g. '[{"name":"amount","type":"number","required":true}]'. type is one of string|number|boolean. Replaces the full set on update/approve.`;
8
- function props(i) {
9
- return i.props === void 0 ? void 0 : i.props;
10
- }
11
- var eventOperations = [
12
- {
13
- group: GROUP,
14
- name: "list",
15
- mutates: false,
16
- summary: "List catalogued events",
17
- description: "List every catalogued event (including pending auto-discovered names). Pass `pending` to show only the unapproved queue.",
18
- params: [{ name: "pending", type: "boolean", description: "Show only pending (unapproved) events." }],
19
- examples: [
20
- { run: "shipeasy metrics events list" },
21
- { note: "Review the auto-discovered queue", run: "shipeasy metrics events list --pending true" }
22
- ],
23
- run: async (client, i) => {
24
- const rows = await client.events.list();
25
- return bool(i, "pending") ? rows.filter((e) => e.pending === 1) : rows;
26
- }
27
- },
28
- {
29
- group: GROUP,
30
- name: "get",
31
- mutates: false,
32
- summary: "Show one catalogued event",
33
- description: "Resolve an event by id, id-prefix, or name and return its full detail.",
34
- params: [{ name: "event", type: "string", description: "Event id or name.", required: true, positional: true }],
35
- examples: [{ run: "shipeasy metrics events get checkout_completed" }],
36
- run: (client, i) => client.events.resolve(i.event)
37
- },
38
- {
39
- group: GROUP,
40
- name: "create",
41
- mutates: true,
42
- summary: "Register a new event",
43
- description: "Register a new event name and (optionally) its typed properties.",
44
- params: [
45
- { name: "name", type: "string", description: "Event name.", required: true, positional: true },
46
- { name: "folder", type: "string", description: "Folder to file it under." },
47
- { name: "description", type: "string", description: "Human-readable description." },
48
- { name: "props", type: "json", description: PROPS_DESC }
49
- ],
50
- examples: [
51
- { run: "shipeasy metrics events create checkout_completed" },
52
- { note: "With a typed, required property", run: `shipeasy metrics events create purchase --props '[{"name":"amount","type":"number","required":true}]'` }
53
- ],
54
- run: (client, i) => client.events.create({
55
- name: i.name,
56
- folder: str(i, "folder") ?? null,
57
- ...i.description ? { description: i.description } : {},
58
- properties: props(i) ?? []
59
- })
60
- },
61
- {
62
- group: GROUP,
63
- name: "update",
64
- mutates: true,
65
- summary: "Update an event",
66
- description: "Update an event's folder, description, or properties (props replace the full set).",
67
- params: [
68
- { name: "event", type: "string", description: "Event id.", required: true, positional: true },
69
- { name: "folder", type: "string", description: "New folder." },
70
- { name: "description", type: "string", description: "New description." },
71
- { name: "props", type: "json", description: PROPS_DESC }
72
- ],
73
- examples: [{ run: "shipeasy metrics events update evt_3f9a --folder checkout" }],
74
- run: (client, i) => {
75
- const patch = {};
76
- if (i.folder !== void 0) patch.folder = i.folder;
77
- if (i.description !== void 0) patch.description = i.description;
78
- const p = props(i);
79
- if (p) patch.properties = p;
80
- return client.events.update(i.event, patch);
81
- }
82
- },
83
- {
84
- group: GROUP,
85
- name: "approve",
86
- mutates: true,
87
- summary: "Approve a pending event",
88
- description: "Promote a pending (auto-discovered) event so metrics can query it. Optionally declare its folder/props on approval.",
89
- params: [
90
- { name: "event", type: "string", description: "Event id.", required: true, positional: true },
91
- { name: "folder", type: "string", description: "Folder to file it under." },
92
- { name: "description", type: "string", description: "Description." },
93
- { name: "props", type: "json", description: PROPS_DESC }
94
- ],
95
- examples: [{ note: "Promote an auto-discovered event", run: "shipeasy metrics events approve evt_3f9a" }],
96
- run: (client, i) => {
97
- const body = {};
98
- if (i.folder !== void 0) body.folder = i.folder;
99
- if (i.description !== void 0) body.description = i.description;
100
- const p = props(i);
101
- if (p) body.properties = p;
102
- return client.events.approve(i.event, body);
103
- }
104
- },
105
- {
106
- group: GROUP,
107
- name: "archive",
108
- mutates: true,
109
- summary: "Archive an event",
110
- description: "Archive (remove) an event from the catalog by id.",
111
- params: [{ name: "event", type: "string", description: "Event id.", required: true, positional: true }],
112
- examples: [{ run: "shipeasy metrics events archive evt_3f9a" }],
113
- run: async (client, i) => {
114
- await client.events.delete(i.event);
115
- return { ok: true, archived: i.event };
116
- }
117
- }
118
- ];
119
- export {
120
- eventOperations
121
- };
@@ -1,28 +0,0 @@
1
- import { Operation } from './types.js';
2
- import '../resources/index.js';
3
- import 'zod/v4/core';
4
- import 'zod';
5
- import '../resources/attributes.js';
6
- import '../transport.js';
7
- import '../resources/i18n.js';
8
- import '../resources/projects.js';
9
- import '../resources/ops.js';
10
- import '../resources/events.js';
11
- import '../resources/metrics.js';
12
- import '../resources/alert-rules.js';
13
- import '../schemas/alert-rules.js';
14
- import '../resources/killswitches.js';
15
- import '../schemas/killswitches.js';
16
- import '../schemas/pagination.js';
17
- import '../resources/universes.js';
18
- import '../schemas/universes.js';
19
- import '../resources/configs.js';
20
- import '../schemas/configs.js';
21
- import '../resources/experiments.js';
22
- import '../schemas/experiments.js';
23
- import '../resources/gates.js';
24
- import '../schemas/gates.js';
25
-
26
- declare const experimentOperations: Operation[];
27
-
28
- export { experimentOperations };
@@ -1,280 +0,0 @@
1
- // src/operations/util.ts
2
- var num = (i, k) => i[k] === void 0 ? void 0 : i[k];
3
- var str = (i, k) => i[k] === void 0 ? void 0 : i[k];
4
- var bool = (i, k) => i[k] === void 0 ? void 0 : i[k];
5
-
6
- // src/operations/experiments.ts
7
- var GROUP = ["release", "experiments"];
8
- var DEFAULT_GROUPS = [
9
- { name: "control", weight: 5e3, params: {} },
10
- { name: "test", weight: 5e3, params: {} }
11
- ];
12
- var AGGREGATIONS = [
13
- "count_users",
14
- "count_events",
15
- "retention_7d",
16
- "retention_30d",
17
- "sum",
18
- "avg"
19
- ];
20
- function buildGoalMetric(i) {
21
- const event = str(i, "successEvent");
22
- if (!event) return void 0;
23
- const agg = str(i, "successAggregation") ?? "count_users";
24
- const value = str(i, "successValue");
25
- switch (agg) {
26
- case "count_users":
27
- return { query: `count_users(${event})` };
28
- case "count_events":
29
- return { query: `count(${event})` };
30
- case "retention_7d":
31
- return { query: `retention_7d(${event})` };
32
- case "retention_30d":
33
- return { query: `retention_30d(${event})` };
34
- case "sum":
35
- case "avg":
36
- if (!value)
37
- throw new Error(
38
- `successAggregation '${agg}' requires successValue \u2014 the numeric event property to ${agg} (e.g. "amount")`
39
- );
40
- return { query: `${agg}(${event}, ${value})` };
41
- default:
42
- throw new Error(`Unknown successAggregation '${agg}'`);
43
- }
44
- }
45
- function parseGuardrails(raw) {
46
- if (!Array.isArray(raw)) return [];
47
- const out = [];
48
- for (const entry of raw) {
49
- if (typeof entry === "string") {
50
- const query = entry.trim();
51
- if (query) out.push({ query });
52
- } else if (entry && typeof entry === "object") {
53
- const o = entry;
54
- const query = typeof o.query === "string" ? o.query.trim() : "";
55
- if (query)
56
- out.push(typeof o.name === "string" && o.name ? { query, name: o.name } : { query });
57
- }
58
- }
59
- return out;
60
- }
61
- function computeVerdict(detail, results) {
62
- if (results.some((r) => r.srm_detected === 1))
63
- return { verdict: "invalid_srm", reason: "sample-ratio mismatch detected" };
64
- if (detail.status !== "running") return { verdict: "not_running", reason: `status=${detail.status}` };
65
- if (!results.length) return { verdict: "wait", reason: "no data yet" };
66
- const withP = results.filter((r) => r.p_value !== null);
67
- if (!withP.length) return { verdict: "wait", reason: "no p-values yet" };
68
- const best = withP.reduce((a, b) => a.p_value < b.p_value ? a : b);
69
- const threshold = detail.significance_threshold ?? 0.05;
70
- if (best.p_value < threshold) return { verdict: "ship" };
71
- const startedAt = detail.startedAt ?? detail.started_at;
72
- const daysRunning = startedAt ? (Date.now() - new Date(startedAt).getTime()) / (1e3 * 60 * 60 * 24) : 0;
73
- return daysRunning < (detail.min_runtime_days ?? 0) ? { verdict: "wait", reason: "min runtime not reached" } : { verdict: "hold" };
74
- }
75
- var experimentOperations = [
76
- {
77
- group: GROUP,
78
- name: "list",
79
- mutates: false,
80
- summary: "List experiments",
81
- description: "Return every experiment (status, universe, allocation).",
82
- params: [],
83
- examples: [{ run: "shipeasy release experiments list" }],
84
- run: (client) => client.experiments.listAll()
85
- },
86
- {
87
- group: GROUP,
88
- name: "create",
89
- mutates: true,
90
- summary: "Create an experiment",
91
- description: "Create an experiment draft with groups, params, optional targeting gate, a success (goal) metric, optional guardrail metrics, and full statistical config. Pass `successEvent` (+ `successAggregation`) to attach the goal metric inline \u2014 required before the experiment can be started. Defaults to a 50/50 control-vs-test split at 100% allocation in the `default` universe. Does NOT start \u2014 call `start`.",
92
- params: [
93
- { name: "name", type: "string", description: "Experiment name.", required: true, positional: true },
94
- { name: "universe", type: "string", description: "Universe name. Defaults to 'default'." },
95
- { name: "allocation", type: "number", description: "Allocation percentage, 0\u2013100. Defaults to 100." },
96
- { name: "groups", type: "json", description: "Groups as JSON [{name,weight,params}]. weight is basis points." },
97
- { name: "params", type: "json", description: "Parameter schema {name: type}." },
98
- { name: "targetingGate", type: "string", description: "Targeting gate name." },
99
- { name: "bucketBy", type: "string", description: "Attribute to bucket by (defaults to the universe unit)." },
100
- { name: "salt", type: "string", description: "Override hash salt." },
101
- { name: "successEvent", type: "string", description: "Event name for the inline goal metric. Attaching one makes the draft startable." },
102
- { name: "successAggregation", type: "string", description: `Goal-metric reducer. Defaults to count_users.`, enum: [...AGGREGATIONS] },
103
- { name: "successValue", type: "string", description: "Numeric event property for sum/avg goal metrics." },
104
- { name: "guardrailMetrics", type: "json", description: "Up to 10 guardrail metrics: JSON array of DSL query strings or {query,name}." },
105
- { name: "significance", type: "number", description: "Significance threshold (0.0001\u20130.5). Defaults to 0.05." },
106
- { name: "minRuntimeDays", type: "number", description: "Minimum runtime in days." },
107
- { name: "minSampleSize", type: "number", description: "Minimum sample size." },
108
- { name: "sequentialTesting", type: "boolean", description: "Enable sequential testing (always-valid p-values; Premium+)." },
109
- { name: "description", type: "string", description: "Human-readable description." }
110
- ],
111
- examples: [
112
- { note: "Default 50/50, full traffic", run: "shipeasy release experiments create pricing-page" },
113
- {
114
- note: "Attach a goal metric so it's startable",
115
- run: "shipeasy release experiments create checkout-cta --universe web --success-event checkout_completed"
116
- }
117
- ],
118
- run: (client, i) => {
119
- const goal_metric = buildGoalMetric(i);
120
- const guardrail_metrics = parseGuardrails(i.guardrailMetrics);
121
- return client.experiments.create({
122
- name: i.name,
123
- universe: str(i, "universe") ?? "default",
124
- allocation_pct: Math.round((num(i, "allocation") ?? 100) * 100),
125
- groups: i.groups ?? DEFAULT_GROUPS,
126
- params: i.params ?? {},
127
- targeting_gate: str(i, "targetingGate") ?? null,
128
- ...str(i, "bucketBy") ? { bucket_by: str(i, "bucketBy") } : {},
129
- ...str(i, "description") !== void 0 ? { description: str(i, "description") } : {},
130
- ...i.salt ? { salt: i.salt } : {},
131
- ...num(i, "significance") !== void 0 ? { significance_threshold: num(i, "significance") } : {},
132
- ...num(i, "minRuntimeDays") !== void 0 ? { min_runtime_days: num(i, "minRuntimeDays") } : {},
133
- ...num(i, "minSampleSize") !== void 0 ? { min_sample_size: num(i, "minSampleSize") } : {},
134
- ...bool(i, "sequentialTesting") !== void 0 ? { sequential_testing: bool(i, "sequentialTesting") } : {},
135
- ...goal_metric ? { goal_metric } : {},
136
- ...guardrail_metrics.length ? { guardrail_metrics } : {}
137
- });
138
- }
139
- },
140
- {
141
- group: GROUP,
142
- name: "update",
143
- mutates: true,
144
- summary: "Update an experiment",
145
- description: "Patch allocation, groups, params, targeting gate, or stats thresholds. Pass `targetingGate` = 'null' to clear it.",
146
- params: [
147
- { name: "name", type: "string", description: "Experiment name.", required: true, positional: true },
148
- { name: "allocation", type: "number", description: "Allocation percentage, 0\u2013100." },
149
- { name: "groups", type: "json", description: "Groups as JSON [{name,weight,params}]." },
150
- { name: "params", type: "json", description: "Parameter schema {name: type}." },
151
- { name: "targetingGate", type: "string", description: "Targeting gate name ('null' to clear)." },
152
- { name: "bucketBy", type: "string", description: "Attribute to bucket by (empty string clears)." },
153
- { name: "successEvent", type: "string", description: "Attach/replace the goal metric from this event." },
154
- { name: "successAggregation", type: "string", description: "Goal-metric reducer (with successEvent).", enum: [...AGGREGATIONS] },
155
- { name: "successValue", type: "string", description: "Numeric event property for sum/avg goal metrics." },
156
- { name: "guardrailMetrics", type: "json", description: "Replace the guardrail set: JSON array of DSL queries or {query,name}." },
157
- { name: "significance", type: "number", description: "Significance threshold (0.0001\u20130.5)." },
158
- { name: "minRuntimeDays", type: "number", description: "Minimum runtime in days." },
159
- { name: "minSampleSize", type: "number", description: "Minimum sample size." },
160
- { name: "sequentialTesting", type: "boolean", description: "Enable sequential testing (always-valid p-values)." }
161
- ],
162
- examples: [
163
- { note: "Dial allocation to 50%", run: "shipeasy release experiments update pricing-page --allocation 50" },
164
- { note: "Clear targeting + tighten significance", run: "shipeasy release experiments update checkout-cta --targeting-gate null --significance 0.01" }
165
- ],
166
- run: async (client, i) => {
167
- const e = await client.experiments.resolve(i.name);
168
- const patch = {};
169
- const allocation = num(i, "allocation");
170
- if (allocation !== void 0) patch.allocation_pct = Math.round(allocation * 100);
171
- if (i.groups !== void 0) patch.groups = i.groups;
172
- if (i.params !== void 0) patch.params = i.params;
173
- if (i.targetingGate !== void 0)
174
- patch.targeting_gate = i.targetingGate === "null" ? null : i.targetingGate;
175
- if (str(i, "bucketBy") !== void 0) patch.bucket_by = str(i, "bucketBy") || null;
176
- if (i.significance !== void 0) patch.significance_threshold = i.significance;
177
- if (i.minRuntimeDays !== void 0) patch.min_runtime_days = i.minRuntimeDays;
178
- if (i.minSampleSize !== void 0) patch.min_sample_size = i.minSampleSize;
179
- if (bool(i, "sequentialTesting") !== void 0) patch.sequential_testing = bool(i, "sequentialTesting");
180
- if (i.guardrailMetrics !== void 0) patch.guardrail_metrics = parseGuardrails(i.guardrailMetrics);
181
- const goal_metric = buildGoalMetric(i);
182
- if (goal_metric) patch.goal_metric = goal_metric;
183
- return client.experiments.update(e.id, patch);
184
- }
185
- },
186
- {
187
- group: GROUP,
188
- name: "start",
189
- mutates: true,
190
- summary: "Start an experiment",
191
- description: "Move an experiment from draft to running.",
192
- params: [{ name: "name", type: "string", description: "Experiment name.", required: true, positional: true }],
193
- examples: [{ run: "shipeasy release experiments start pricing-page" }],
194
- run: async (client, i) => {
195
- const e = await client.experiments.resolve(i.name);
196
- return client.experiments.start(e.id);
197
- }
198
- },
199
- {
200
- group: GROUP,
201
- name: "stop",
202
- mutates: true,
203
- summary: "Stop a running experiment",
204
- description: "Stop a running experiment. Optionally name a `promoteGroup` to record an intent note (actual promotion happens in the dashboard).",
205
- params: [
206
- { name: "name", type: "string", description: "Experiment name.", required: true, positional: true },
207
- { name: "promoteGroup", type: "string", description: "Group to note as the winner to promote." }
208
- ],
209
- examples: [{ run: "shipeasy release experiments stop pricing-page" }],
210
- run: async (client, i) => {
211
- const e = await client.experiments.resolve(i.name);
212
- const result = await client.experiments.stop(e.id);
213
- const promote = str(i, "promoteGroup");
214
- return {
215
- ...result,
216
- ...promote ? {
217
- promote_group_note: `Manual promotion needed: promote group '${promote}' in the dashboard or via PATCH /api/admin/experiments/${e.id}.`
218
- } : {}
219
- };
220
- }
221
- },
222
- {
223
- group: GROUP,
224
- name: "archive",
225
- mutates: true,
226
- summary: "Archive a stopped experiment",
227
- description: "Archive (soft-delete) a stopped experiment, hiding it from the default list while preserving results.",
228
- params: [{ name: "name", type: "string", description: "Experiment name.", required: true, positional: true }],
229
- examples: [{ run: "shipeasy release experiments archive pricing-page" }],
230
- run: async (client, i) => {
231
- const e = await client.experiments.resolve(i.name);
232
- return client.experiments.archive(e.id);
233
- }
234
- },
235
- {
236
- group: GROUP,
237
- name: "restore",
238
- mutates: true,
239
- summary: "Restore an archived experiment to draft",
240
- description: "Restore a soft-deleted (archived) experiment back to `draft`. Allowed only if it never started; one that already ran must be cloned instead. Preserves the goal metric.",
241
- params: [{ name: "name", type: "string", description: "Experiment name.", required: true, positional: true }],
242
- examples: [{ run: "shipeasy release experiments restore pricing-page" }],
243
- run: async (client, i) => {
244
- const e = await client.experiments.resolve(i.name);
245
- return client.experiments.restore(e.id);
246
- }
247
- },
248
- {
249
- group: GROUP,
250
- name: "reanalyze",
251
- mutates: true,
252
- summary: "Re-run the analysis pass",
253
- description: "Trigger a fresh analysis pass for an experiment.",
254
- params: [{ name: "name", type: "string", description: "Experiment name.", required: true, positional: true }],
255
- examples: [{ run: "shipeasy release experiments reanalyze pricing-page" }],
256
- run: async (client, i) => {
257
- const e = await client.experiments.resolve(i.name);
258
- return client.experiments.reanalyze(e.id);
259
- }
260
- },
261
- {
262
- group: GROUP,
263
- name: "status",
264
- mutates: false,
265
- summary: "Show experiment status, verdict, and latest results",
266
- description: "Return the experiment, its latest per-metric results (enrolment, deltas, p-values), and a ship/hold/wait/not_running/invalid_srm verdict computed from the goal metric vs the significance threshold and minimum runtime.",
267
- params: [{ name: "name", type: "string", description: "Experiment name.", required: true, positional: true }],
268
- examples: [{ run: "shipeasy release experiments status pricing-page" }],
269
- run: async (client, i) => {
270
- const e = await client.experiments.resolve(i.name);
271
- const detail = await client.experiments.get(e.id).catch(() => e);
272
- const results = await client.experiments.results(e.id).catch(() => []);
273
- const { verdict, reason } = computeVerdict(detail, results);
274
- return { verdict, ...reason ? { reason } : {}, experiment: detail, results };
275
- }
276
- }
277
- ];
278
- export {
279
- experimentOperations
280
- };