@shipeasy/openapi 1.5.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +24 -0
- package/dist/index.js +1913 -1
- package/dist/node-context.d.ts +37 -0
- package/dist/node-context.js +56 -0
- package/dist/operations/attributes.d.ts +33 -0
- package/dist/operations/attributes.js +16 -0
- package/dist/operations/cli-adapter.d.ts +35 -0
- package/dist/operations/cli-adapter.js +91 -0
- package/dist/operations/coerce.d.ts +32 -0
- package/dist/operations/coerce.js +53 -0
- package/dist/operations/configs.d.ts +28 -0
- package/dist/operations/configs.js +119 -0
- package/dist/operations/docs.d.ts +37 -0
- package/dist/operations/docs.js +39 -0
- package/dist/operations/events.d.ts +28 -0
- package/dist/operations/events.js +121 -0
- package/dist/operations/experiments.d.ts +28 -0
- package/dist/operations/experiments.js +280 -0
- package/dist/operations/gates.d.ts +28 -0
- package/dist/operations/gates.js +132 -0
- package/dist/operations/i18n.d.ts +28 -0
- package/dist/operations/i18n.js +121 -0
- package/dist/operations/index.d.ts +63 -0
- package/dist/operations/index.js +1751 -0
- package/dist/operations/killswitches.d.ts +28 -0
- package/dist/operations/killswitches.js +126 -0
- package/dist/operations/mcp-adapter.d.ts +35 -0
- package/dist/operations/mcp-adapter.js +94 -0
- package/dist/operations/metrics.d.ts +30 -0
- package/dist/operations/metrics.js +114 -0
- package/dist/operations/ops.d.ts +28 -0
- package/dist/operations/ops.js +313 -0
- package/dist/operations/projects.d.ts +28 -0
- package/dist/operations/projects.js +36 -0
- package/dist/operations/sdk-docs.d.ts +28 -0
- package/dist/operations/sdk-docs.js +160 -0
- package/dist/operations/types.d.ts +138 -0
- package/dist/operations/types.js +9 -0
- package/dist/operations/universes.d.ts +28 -0
- package/dist/operations/universes.js +93 -0
- package/dist/operations/util.d.ts +33 -0
- package/dist/operations/util.js +9 -0
- package/dist/resources/attributes.d.ts +19 -0
- package/dist/resources/attributes.js +10 -0
- package/dist/resources/events.d.ts +61 -0
- package/dist/resources/events.js +42 -0
- package/dist/resources/i18n.d.ts +84 -0
- package/dist/resources/i18n.js +60 -0
- package/dist/resources/index.d.ts +22 -4
- package/dist/resources/index.js +173 -1
- package/dist/resources/metrics.d.ts +54 -0
- package/dist/resources/metrics.js +40 -0
- package/dist/resources/ops.d.ts +88 -0
- package/dist/resources/ops.js +36 -0
- package/dist/resources/projects.d.ts +54 -0
- package/dist/resources/projects.js +14 -0
- package/package.json +10 -1
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { AdminClient } from '../resources/index.js';
|
|
2
|
+
import 'zod/v4/core';
|
|
3
|
+
import 'zod';
|
|
4
|
+
import '../resources/attributes.js';
|
|
5
|
+
import '../transport.js';
|
|
6
|
+
import '../resources/i18n.js';
|
|
7
|
+
import '../resources/projects.js';
|
|
8
|
+
import '../resources/ops.js';
|
|
9
|
+
import '../resources/events.js';
|
|
10
|
+
import '../resources/metrics.js';
|
|
11
|
+
import '../resources/alert-rules.js';
|
|
12
|
+
import '../schemas/alert-rules.js';
|
|
13
|
+
import '../resources/killswitches.js';
|
|
14
|
+
import '../schemas/killswitches.js';
|
|
15
|
+
import '../schemas/pagination.js';
|
|
16
|
+
import '../resources/universes.js';
|
|
17
|
+
import '../schemas/universes.js';
|
|
18
|
+
import '../resources/configs.js';
|
|
19
|
+
import '../schemas/configs.js';
|
|
20
|
+
import '../resources/experiments.js';
|
|
21
|
+
import '../schemas/experiments.js';
|
|
22
|
+
import '../resources/gates.js';
|
|
23
|
+
import '../schemas/gates.js';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Operation registry — the single source of truth that drives BOTH
|
|
27
|
+
* `@shipeasy/cli` (commander subcommands) and `@shipeasy/mcp` (MCP tools),
|
|
28
|
+
* plus their generated docs.
|
|
29
|
+
*
|
|
30
|
+
* Today each surface is hand-maintained: the CLI hand-builds commander
|
|
31
|
+
* commands in `packages/cli/src/commands/*`, the MCP server hand-writes tool
|
|
32
|
+
* JSON Schemas in `packages/mcp/src/tools/schema.ts` and a 60-branch dispatch
|
|
33
|
+
* in `server.ts`, and the percent→basis-points + `JSON.parse(rules)` mapping
|
|
34
|
+
* is written *identically* in both. An Operation collapses that into one
|
|
35
|
+
* definition: the params describe the ergonomic facade once, `run` performs
|
|
36
|
+
* the facade→wire mapping once, and the adapters in this folder project it
|
|
37
|
+
* onto each surface.
|
|
38
|
+
*/
|
|
39
|
+
/** Param value kinds. `json` arrives as a JSON string on both surfaces and is parsed by the framework. */
|
|
40
|
+
type ParamType = "string" | "number" | "boolean" | "json";
|
|
41
|
+
interface Param {
|
|
42
|
+
/** Field name — also the CLI flag (`--<name>`) or positional, and the MCP property key. */
|
|
43
|
+
name: string;
|
|
44
|
+
type: ParamType;
|
|
45
|
+
/** Shown in CLI `--help`, the MCP tool input schema, and generated docs. */
|
|
46
|
+
description: string;
|
|
47
|
+
required?: boolean;
|
|
48
|
+
/** CLI only: render as a positional `<name>` argument instead of a `--flag`. Ignored by MCP. */
|
|
49
|
+
positional?: boolean;
|
|
50
|
+
/** Restrict to a fixed set (MCP `enum`, CLI validated in coercion). */
|
|
51
|
+
enum?: string[];
|
|
52
|
+
}
|
|
53
|
+
interface OpExample {
|
|
54
|
+
/** Why you'd run this. */
|
|
55
|
+
note?: string;
|
|
56
|
+
/** A copy-pasteable command line. */
|
|
57
|
+
run: string;
|
|
58
|
+
}
|
|
59
|
+
/** Coerced facade input handed to `run` — keys are param names, values coerced by `ParamType`. */
|
|
60
|
+
type OpInput = Record<string, unknown>;
|
|
61
|
+
interface Operation<O = unknown> {
|
|
62
|
+
/**
|
|
63
|
+
* Command-path segments BEFORE the verb, e.g. `["release", "flags"]`.
|
|
64
|
+
* This is the exact CLI nesting; nested resources just add segments
|
|
65
|
+
* (`["release", "experiments", "universes"]`). Drives grouping + naming.
|
|
66
|
+
*/
|
|
67
|
+
group: string[];
|
|
68
|
+
/**
|
|
69
|
+
* The verb, e.g. `create`. Authored ONCE — every surface name derives from
|
|
70
|
+
* `group` + `name` so they can never drift:
|
|
71
|
+
* id = `release.flags.create` (opId — `.`-joined)
|
|
72
|
+
* CLI = `release flags create` (opCli — space-joined, the real invocation)
|
|
73
|
+
* MCP = `release_flags_create` (opMcpName — `_`-joined; MCP names can't have spaces)
|
|
74
|
+
*/
|
|
75
|
+
name: string;
|
|
76
|
+
/** True for writes — the CLI/MCP adapter enforces `.shipeasy` binding before running. */
|
|
77
|
+
mutates: boolean;
|
|
78
|
+
/** One-liner — CLI `.description()` and the docs heading. */
|
|
79
|
+
summary: string;
|
|
80
|
+
/** Long-form — the MCP tool `description` and the docs body. */
|
|
81
|
+
description: string;
|
|
82
|
+
params: Param[];
|
|
83
|
+
examples?: OpExample[];
|
|
84
|
+
/**
|
|
85
|
+
* The single source of behaviour: coerced facade input → typed AdminClient
|
|
86
|
+
* call → raw data. Returns data (never prints) so every surface can format
|
|
87
|
+
* it its own way (CLI table, MCP JSON envelope, …).
|
|
88
|
+
*/
|
|
89
|
+
run: (client: AdminClient, input: OpInput) => Promise<O>;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Structural subset of commander's `Command`. The CLI adapter targets this so
|
|
93
|
+
* `@shipeasy/openapi` never has to depend on `commander` — commander's real
|
|
94
|
+
* `Command` satisfies it.
|
|
95
|
+
*/
|
|
96
|
+
interface CommandLike {
|
|
97
|
+
command(nameAndArgs: string): CommandLike;
|
|
98
|
+
description(text: string): CommandLike;
|
|
99
|
+
option(flags: string, description?: string, defaultValue?: unknown): CommandLike;
|
|
100
|
+
action(fn: (...args: unknown[]) => void | Promise<void>): CommandLike;
|
|
101
|
+
}
|
|
102
|
+
/** Everything the CLI surface injects so the adapter stays free of process/chalk/IO. */
|
|
103
|
+
interface CliContext {
|
|
104
|
+
/** Resolve the typed admin client; `mutates` triggers the `requireBinding` guard. */
|
|
105
|
+
getClient: (o: {
|
|
106
|
+
mutates: boolean;
|
|
107
|
+
projectOverride?: string;
|
|
108
|
+
}) => AdminClient;
|
|
109
|
+
/** Print machine output (`--json`). */
|
|
110
|
+
printJson: (data: unknown) => void;
|
|
111
|
+
/** Print human output for a successful op (table / one-liner). */
|
|
112
|
+
printResult: (op: Operation, data: unknown, input: OpInput) => void;
|
|
113
|
+
/** Terminal error handler (prints + exits). */
|
|
114
|
+
onError: (e: unknown) => void;
|
|
115
|
+
}
|
|
116
|
+
type Named = Pick<Operation, "group" | "name">;
|
|
117
|
+
/** Stable id, e.g. `release.flags.create`. */
|
|
118
|
+
declare const opId: (op: Named) => string;
|
|
119
|
+
/** MCP tool name, e.g. `release_flags_create` — the CLI path with `_` for each space. */
|
|
120
|
+
declare const opMcpName: (op: Named) => string;
|
|
121
|
+
/** CLI invocation, e.g. `release flags create` (full command path). */
|
|
122
|
+
declare const opCli: (op: Named) => string;
|
|
123
|
+
/** Plain MCP `Tool` shape — structurally compatible with `@modelcontextprotocol/sdk`'s `Tool`. */
|
|
124
|
+
interface McpTool {
|
|
125
|
+
name: string;
|
|
126
|
+
description: string;
|
|
127
|
+
inputSchema: {
|
|
128
|
+
type: "object";
|
|
129
|
+
required?: string[];
|
|
130
|
+
properties: Record<string, {
|
|
131
|
+
type: string;
|
|
132
|
+
description?: string;
|
|
133
|
+
enum?: string[];
|
|
134
|
+
}>;
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export { type CliContext, type CommandLike, type McpTool, type OpExample, type OpInput, type Operation, type Param, type ParamType, opCli, opId, opMcpName };
|
|
@@ -0,0 +1,28 @@
|
|
|
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 universeOperations: Operation[];
|
|
27
|
+
|
|
28
|
+
export { universeOperations };
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// src/transport.ts
|
|
2
|
+
var ApiError = class extends Error {
|
|
3
|
+
constructor(message, status) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.status = status;
|
|
6
|
+
this.name = "ApiError";
|
|
7
|
+
}
|
|
8
|
+
status;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
// src/operations/util.ts
|
|
12
|
+
var str = (i, k) => i[k] === void 0 ? void 0 : i[k];
|
|
13
|
+
|
|
14
|
+
// src/operations/universes.ts
|
|
15
|
+
var GROUP = ["release", "experiments", "universes"];
|
|
16
|
+
function parseHoldout(raw) {
|
|
17
|
+
if (raw === void 0 || raw === "" || raw === "null") return null;
|
|
18
|
+
const [lo, hi] = raw.split(",").map((s) => Number(s.trim()));
|
|
19
|
+
if (!Number.isFinite(lo) || !Number.isFinite(hi)) {
|
|
20
|
+
throw new ApiError("holdout must be 'lo,hi' integers in [0,9999]", 400);
|
|
21
|
+
}
|
|
22
|
+
return [lo, hi];
|
|
23
|
+
}
|
|
24
|
+
var universeOperations = [
|
|
25
|
+
{
|
|
26
|
+
group: GROUP,
|
|
27
|
+
name: "list",
|
|
28
|
+
mutates: false,
|
|
29
|
+
summary: "List universes",
|
|
30
|
+
description: "Return every experiment universe (allocation %, holdout %).",
|
|
31
|
+
params: [],
|
|
32
|
+
examples: [{ run: "shipeasy release experiments universes list" }],
|
|
33
|
+
run: (client) => client.universes.listAll()
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
group: GROUP,
|
|
37
|
+
name: "create",
|
|
38
|
+
mutates: true,
|
|
39
|
+
summary: "Create a universe",
|
|
40
|
+
description: "Create an experiment universe (container + optional holdout).",
|
|
41
|
+
params: [
|
|
42
|
+
{ name: "name", type: "string", description: "Universe name.", required: true, positional: true },
|
|
43
|
+
{ name: "unitType", type: "string", description: "Unit type (e.g. user_id, account_id). Defaults to user_id." },
|
|
44
|
+
{ name: "holdout", type: "string", description: "Holdout range as 'lo,hi' (0\u20139999); omit for no holdout." }
|
|
45
|
+
],
|
|
46
|
+
examples: [
|
|
47
|
+
{ note: "Default user_id unit, no holdout", run: "shipeasy release experiments universes create web" },
|
|
48
|
+
{ note: "Holdout 0\u2013999, keyed on account_id", run: "shipeasy release experiments universes create web --unit-type account_id --holdout 0,999" }
|
|
49
|
+
],
|
|
50
|
+
run: (client, i) => client.universes.create({
|
|
51
|
+
name: i.name,
|
|
52
|
+
unit_type: str(i, "unitType") ?? "user_id",
|
|
53
|
+
holdout_range: parseHoldout(str(i, "holdout"))
|
|
54
|
+
})
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
group: GROUP,
|
|
58
|
+
name: "update",
|
|
59
|
+
mutates: true,
|
|
60
|
+
summary: "Update a universe's holdout range",
|
|
61
|
+
description: "Update the holdout range. Pass 'null' to clear the holdout.",
|
|
62
|
+
params: [
|
|
63
|
+
{ name: "name", type: "string", description: "Universe name.", required: true, positional: true },
|
|
64
|
+
{ name: "holdout", type: "string", description: "Holdout range as 'lo,hi' (0\u20139999), or 'null' to clear." }
|
|
65
|
+
],
|
|
66
|
+
examples: [
|
|
67
|
+
{ note: "Set a 5% holdout", run: "shipeasy release experiments universes update web --holdout 0,499" },
|
|
68
|
+
{ note: "Clear the holdout", run: "shipeasy release experiments universes update web --holdout null" }
|
|
69
|
+
],
|
|
70
|
+
run: async (client, i) => {
|
|
71
|
+
const x = await client.universes.resolve(i.name);
|
|
72
|
+
const patch = i.holdout === void 0 ? {} : { holdout_range: parseHoldout(str(i, "holdout")) };
|
|
73
|
+
return client.universes.update(x.id, patch);
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
group: GROUP,
|
|
78
|
+
name: "archive",
|
|
79
|
+
mutates: true,
|
|
80
|
+
summary: "Archive a universe",
|
|
81
|
+
description: "Archive (soft-delete) a universe by name.",
|
|
82
|
+
params: [{ name: "name", type: "string", description: "Universe name.", required: true, positional: true }],
|
|
83
|
+
examples: [{ run: "shipeasy release experiments universes archive web" }],
|
|
84
|
+
run: async (client, i) => {
|
|
85
|
+
const x = await client.universes.resolve(i.name);
|
|
86
|
+
await client.universes.delete(x.id);
|
|
87
|
+
return { ok: true, archived: i.name };
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
];
|
|
91
|
+
export {
|
|
92
|
+
universeOperations
|
|
93
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { OpInput } 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
|
+
/** Read a coerced number param (already `Number()`-ed by the framework), or undefined. */
|
|
27
|
+
declare const num: (i: OpInput, k: string) => number | undefined;
|
|
28
|
+
/** Read a string param, or undefined. */
|
|
29
|
+
declare const str: (i: OpInput, k: string) => string | undefined;
|
|
30
|
+
/** Read a boolean param, or undefined. */
|
|
31
|
+
declare const bool: (i: OpInput, k: string) => boolean | undefined;
|
|
32
|
+
|
|
33
|
+
export { bool, num, str };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Transport } from '../transport.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Targeting attributes — the auto-inferred user-attribute schema the dashboard
|
|
5
|
+
* surfaces for gate/experiment targeting. Read-only; this exists so the typed
|
|
6
|
+
* `attributes list` op can replace the `attributes` kind of the old MCP-only
|
|
7
|
+
* "generic read" (`list_resources`), which is being deleted (see doc 21 §A4.3).
|
|
8
|
+
*/
|
|
9
|
+
interface Attribute {
|
|
10
|
+
name: string;
|
|
11
|
+
type?: string;
|
|
12
|
+
[key: string]: unknown;
|
|
13
|
+
}
|
|
14
|
+
interface AttributesClient {
|
|
15
|
+
list(): Promise<Attribute[]>;
|
|
16
|
+
}
|
|
17
|
+
declare function attributesClient(t: Transport): AttributesClient;
|
|
18
|
+
|
|
19
|
+
export { type Attribute, type AttributesClient, attributesClient };
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Transport } from '../transport.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Event catalog — the registry of event names (and their typed properties) that
|
|
5
|
+
* metric queries reference. `/collect` auto-discovers unknown names as
|
|
6
|
+
* `pending` rows; `approve` promotes them to usable. Property `name:type`
|
|
7
|
+
* parsing is a CLI/MCP facade concern; the resource takes the already-parsed
|
|
8
|
+
* `properties` array.
|
|
9
|
+
*/
|
|
10
|
+
interface EventProperty {
|
|
11
|
+
name: string;
|
|
12
|
+
type: "string" | "number" | "boolean";
|
|
13
|
+
required: boolean;
|
|
14
|
+
description?: string;
|
|
15
|
+
}
|
|
16
|
+
interface CatalogEvent {
|
|
17
|
+
id: string;
|
|
18
|
+
name: string;
|
|
19
|
+
folder: string | null;
|
|
20
|
+
description: string | null;
|
|
21
|
+
properties: EventProperty[];
|
|
22
|
+
pending: number;
|
|
23
|
+
createdAt: string;
|
|
24
|
+
[key: string]: unknown;
|
|
25
|
+
}
|
|
26
|
+
interface EventCreateInput {
|
|
27
|
+
name: string;
|
|
28
|
+
folder?: string | null;
|
|
29
|
+
description?: string;
|
|
30
|
+
properties?: EventProperty[];
|
|
31
|
+
}
|
|
32
|
+
interface EventUpdateInput {
|
|
33
|
+
folder?: string | null;
|
|
34
|
+
description?: string;
|
|
35
|
+
/** Replaces the full property set (no merge). */
|
|
36
|
+
properties?: EventProperty[];
|
|
37
|
+
}
|
|
38
|
+
interface EventsClient {
|
|
39
|
+
list(): Promise<CatalogEvent[]>;
|
|
40
|
+
get(id: string): Promise<CatalogEvent>;
|
|
41
|
+
create(input: EventCreateInput): Promise<{
|
|
42
|
+
id: string;
|
|
43
|
+
name: string;
|
|
44
|
+
}>;
|
|
45
|
+
update(id: string, input: EventUpdateInput): Promise<{
|
|
46
|
+
id: string;
|
|
47
|
+
}>;
|
|
48
|
+
/** Promote a pending (auto-discovered) event so metrics can query it. */
|
|
49
|
+
approve(id: string, input?: EventUpdateInput): Promise<{
|
|
50
|
+
id: string;
|
|
51
|
+
}>;
|
|
52
|
+
/** Soft-delete (the user-facing verb is `archive`). */
|
|
53
|
+
delete(id: string): Promise<{
|
|
54
|
+
ok: true;
|
|
55
|
+
}>;
|
|
56
|
+
/** Resolve by exact id, unique id-prefix, or exact (unique) name. */
|
|
57
|
+
resolve(idOrName: string): Promise<CatalogEvent>;
|
|
58
|
+
}
|
|
59
|
+
declare function eventsClient(t: Transport): EventsClient;
|
|
60
|
+
|
|
61
|
+
export { type CatalogEvent, type EventCreateInput, type EventProperty, type EventUpdateInput, type EventsClient, eventsClient };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// src/transport.ts
|
|
2
|
+
var ApiError = class extends Error {
|
|
3
|
+
constructor(message, status) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.status = status;
|
|
6
|
+
this.name = "ApiError";
|
|
7
|
+
}
|
|
8
|
+
status;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
// src/resources/events.ts
|
|
12
|
+
var BASE = "/api/admin/events";
|
|
13
|
+
function eventsClient(t) {
|
|
14
|
+
async function list() {
|
|
15
|
+
return t.request("GET", BASE);
|
|
16
|
+
}
|
|
17
|
+
async function resolve(idOrName) {
|
|
18
|
+
const all = await list();
|
|
19
|
+
const byId = all.find((e) => e.id === idOrName);
|
|
20
|
+
if (byId) return byId;
|
|
21
|
+
const byPrefix = all.filter((e) => e.id.startsWith(idOrName));
|
|
22
|
+
if (byPrefix.length === 1) return byPrefix[0];
|
|
23
|
+
if (byPrefix.length > 1) throw new ApiError(`Event id prefix '${idOrName}' is ambiguous`, 400);
|
|
24
|
+
const byName = all.filter((e) => e.name === idOrName);
|
|
25
|
+
if (byName.length === 1) return byName[0];
|
|
26
|
+
if (byName.length > 1)
|
|
27
|
+
throw new ApiError(`Event name '${idOrName}' is ambiguous \u2014 pass an id`, 400);
|
|
28
|
+
throw new ApiError(`Event '${idOrName}' not found`, 404);
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
list,
|
|
32
|
+
resolve,
|
|
33
|
+
get: (id) => t.request("GET", `${BASE}/${id}`),
|
|
34
|
+
create: (input) => t.request("POST", BASE, input),
|
|
35
|
+
update: (id, input) => t.request("PATCH", `${BASE}/${id}`, input),
|
|
36
|
+
approve: (id, input = {}) => t.request("POST", `${BASE}/${id}/approve`, input),
|
|
37
|
+
delete: (id) => t.request("DELETE", `${BASE}/${id}`)
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export {
|
|
41
|
+
eventsClient
|
|
42
|
+
};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { Transport } from '../transport.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* i18n (String Manager) — the API-only parts that are worker-safe and shared
|
|
5
|
+
* between CLI and MCP: locale profiles, the insert-only key push, single-key
|
|
6
|
+
* update, chunk publish, and the read-only key/draft listings.
|
|
7
|
+
*
|
|
8
|
+
* The fs/AST parts (`scan`, `validate`, `install-loader`, `codemod`) stay
|
|
9
|
+
* hand-written in the fs-having consumers and are NEVER added here — the
|
|
10
|
+
* registry is bundled into a Worker that can't load `node:fs` or the TS AST.
|
|
11
|
+
*/
|
|
12
|
+
interface I18nProfile {
|
|
13
|
+
id: string;
|
|
14
|
+
name: string;
|
|
15
|
+
locales?: string[];
|
|
16
|
+
default_locale?: string;
|
|
17
|
+
}
|
|
18
|
+
interface I18nKey {
|
|
19
|
+
id: string;
|
|
20
|
+
key: string;
|
|
21
|
+
value?: string;
|
|
22
|
+
[key: string]: unknown;
|
|
23
|
+
}
|
|
24
|
+
interface I18nDraft {
|
|
25
|
+
id: string;
|
|
26
|
+
[key: string]: unknown;
|
|
27
|
+
}
|
|
28
|
+
interface I18nPushResult {
|
|
29
|
+
added?: string[];
|
|
30
|
+
skipped?: string[];
|
|
31
|
+
pushed_count?: number;
|
|
32
|
+
skipped_count?: number;
|
|
33
|
+
}
|
|
34
|
+
interface I18nClient {
|
|
35
|
+
listProfiles(): Promise<I18nProfile[]>;
|
|
36
|
+
createProfile(input: {
|
|
37
|
+
name: string;
|
|
38
|
+
locales?: string[];
|
|
39
|
+
default_locale?: string;
|
|
40
|
+
}): Promise<{
|
|
41
|
+
id: string;
|
|
42
|
+
name: string;
|
|
43
|
+
}>;
|
|
44
|
+
/** Resolve a profile by exact name (the user-facing handle). */
|
|
45
|
+
resolveProfile(name: string): Promise<I18nProfile>;
|
|
46
|
+
/** Insert-only key push (existing keys are left untouched). */
|
|
47
|
+
pushKeys(input: {
|
|
48
|
+
profile_id: string;
|
|
49
|
+
chunk?: string;
|
|
50
|
+
keys: {
|
|
51
|
+
key: string;
|
|
52
|
+
value: string;
|
|
53
|
+
}[];
|
|
54
|
+
}): Promise<I18nPushResult>;
|
|
55
|
+
/** List keys for a profile (optionally a name prefix). */
|
|
56
|
+
listKeys(query?: {
|
|
57
|
+
profile_id?: string;
|
|
58
|
+
prefix?: string;
|
|
59
|
+
limit?: number;
|
|
60
|
+
}): Promise<I18nKey[]>;
|
|
61
|
+
/** Update one existing key's value (the only overwrite path). */
|
|
62
|
+
updateKey(id: string, input: {
|
|
63
|
+
value: string;
|
|
64
|
+
description?: string;
|
|
65
|
+
}): Promise<{
|
|
66
|
+
id: string;
|
|
67
|
+
}>;
|
|
68
|
+
/** Resolve a key by exact name within a profile, then PUT its new value. */
|
|
69
|
+
updateKeyByName(profileId: string, key: string, input: {
|
|
70
|
+
value: string;
|
|
71
|
+
description?: string;
|
|
72
|
+
}): Promise<{
|
|
73
|
+
id: string;
|
|
74
|
+
}>;
|
|
75
|
+
/** List staged drafts (the read-only `drafts` kind from the old generic read). */
|
|
76
|
+
listDrafts(): Promise<I18nDraft[]>;
|
|
77
|
+
/** Publish a profile chunk (rebuild KV manifest + purge CDN). */
|
|
78
|
+
publish(profileId: string, input: {
|
|
79
|
+
chunk?: string;
|
|
80
|
+
}): Promise<unknown>;
|
|
81
|
+
}
|
|
82
|
+
declare function i18nClient(t: Transport): I18nClient;
|
|
83
|
+
|
|
84
|
+
export { type I18nClient, type I18nDraft, type I18nKey, type I18nProfile, type I18nPushResult, i18nClient };
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// src/transport.ts
|
|
2
|
+
var ApiError = class extends Error {
|
|
3
|
+
constructor(message, status) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.status = status;
|
|
6
|
+
this.name = "ApiError";
|
|
7
|
+
}
|
|
8
|
+
status;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
// src/resources/i18n.ts
|
|
12
|
+
var PROFILES = "/api/admin/i18n/profiles";
|
|
13
|
+
var KEYS = "/api/admin/i18n/keys";
|
|
14
|
+
var DRAFTS = "/api/admin/i18n/drafts";
|
|
15
|
+
function i18nClient(t) {
|
|
16
|
+
async function listProfiles() {
|
|
17
|
+
return t.request("GET", PROFILES);
|
|
18
|
+
}
|
|
19
|
+
async function resolveProfile(name) {
|
|
20
|
+
const all = await listProfiles();
|
|
21
|
+
const found = all.find((p) => p.name === name);
|
|
22
|
+
if (!found)
|
|
23
|
+
throw new ApiError(
|
|
24
|
+
`Profile '${name}' not found. Existing: ${all.map((p) => p.name).join(", ") || "(none)"}`,
|
|
25
|
+
404
|
|
26
|
+
);
|
|
27
|
+
return found;
|
|
28
|
+
}
|
|
29
|
+
async function listKeys(query = {}) {
|
|
30
|
+
const q = {};
|
|
31
|
+
if (query.profile_id) q.profile_id = query.profile_id;
|
|
32
|
+
if (query.prefix) q.prefix = query.prefix;
|
|
33
|
+
if (query.limit !== void 0) q.limit = String(query.limit);
|
|
34
|
+
const res = await t.request("GET", KEYS, void 0, q);
|
|
35
|
+
return Array.isArray(res) ? res : res.keys;
|
|
36
|
+
}
|
|
37
|
+
function updateKey(id, input) {
|
|
38
|
+
return t.request("PUT", `${KEYS}/${id}`, input);
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
listProfiles,
|
|
42
|
+
resolveProfile,
|
|
43
|
+
listKeys,
|
|
44
|
+
updateKey,
|
|
45
|
+
createProfile: (input) => t.request("POST", PROFILES, input),
|
|
46
|
+
pushKeys: (input) => t.request("POST", KEYS, input),
|
|
47
|
+
updateKeyByName: async (profileId, key, input) => {
|
|
48
|
+
const rows = await listKeys({ profile_id: profileId, prefix: key, limit: 500 });
|
|
49
|
+
const match = rows.find((k) => k.key === key);
|
|
50
|
+
if (!match)
|
|
51
|
+
throw new ApiError(`Key '${key}' not found in profile. Add it first with i18n push.`, 404);
|
|
52
|
+
return updateKey(match.id, input);
|
|
53
|
+
},
|
|
54
|
+
listDrafts: () => t.request("GET", DRAFTS),
|
|
55
|
+
publish: (profileId, input) => t.request("POST", `${PROFILES}/${profileId}/publish`, { chunk: input.chunk ?? "default" })
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
export {
|
|
59
|
+
i18nClient
|
|
60
|
+
};
|
|
@@ -1,7 +1,17 @@
|
|
|
1
|
-
import { Transport } from '../transport.js';
|
|
2
|
-
import '../schemas/pagination.js';
|
|
3
1
|
import * as zod_v4_core from 'zod/v4/core';
|
|
4
2
|
import * as zod from 'zod';
|
|
3
|
+
import { AttributesClient } from './attributes.js';
|
|
4
|
+
export { attributesClient } from './attributes.js';
|
|
5
|
+
import { I18nClient } from './i18n.js';
|
|
6
|
+
export { i18nClient } from './i18n.js';
|
|
7
|
+
import { ProjectsClient } from './projects.js';
|
|
8
|
+
export { projectsClient } from './projects.js';
|
|
9
|
+
import { OpsClient } from './ops.js';
|
|
10
|
+
export { opsClient } from './ops.js';
|
|
11
|
+
import { EventsClient } from './events.js';
|
|
12
|
+
export { eventsClient } from './events.js';
|
|
13
|
+
import { MetricsClient } from './metrics.js';
|
|
14
|
+
export { metricsClient } from './metrics.js';
|
|
5
15
|
import { AlertRulesClient } from './alert-rules.js';
|
|
6
16
|
export { alertRulesClient } from './alert-rules.js';
|
|
7
17
|
import { KillswitchesClient } from './killswitches.js';
|
|
@@ -14,12 +24,14 @@ import { ExperimentsClient } from './experiments.js';
|
|
|
14
24
|
export { experimentsClient, experimentsResource } from './experiments.js';
|
|
15
25
|
import { GatesClient } from './gates.js';
|
|
16
26
|
export { gatesClient, gatesResource } from './gates.js';
|
|
17
|
-
import '../
|
|
18
|
-
import '../schemas/universes.js';
|
|
27
|
+
import { Transport } from '../transport.js';
|
|
19
28
|
import '../schemas/alert-rules.js';
|
|
20
29
|
import '../schemas/killswitches.js';
|
|
30
|
+
import '../schemas/pagination.js';
|
|
31
|
+
import '../schemas/universes.js';
|
|
21
32
|
import '../schemas/configs.js';
|
|
22
33
|
import '../schemas/experiments.js';
|
|
34
|
+
import '../schemas/gates.js';
|
|
23
35
|
|
|
24
36
|
/**
|
|
25
37
|
* Aggregate admin client. Each resource lives in its own file under
|
|
@@ -36,6 +48,12 @@ declare function createAdminClient(transport: Transport): {
|
|
|
36
48
|
universes: UniversesClient;
|
|
37
49
|
killswitches: KillswitchesClient;
|
|
38
50
|
alertRules: AlertRulesClient;
|
|
51
|
+
metrics: MetricsClient;
|
|
52
|
+
events: EventsClient;
|
|
53
|
+
ops: OpsClient;
|
|
54
|
+
projects: ProjectsClient;
|
|
55
|
+
i18n: I18nClient;
|
|
56
|
+
attributes: AttributesClient;
|
|
39
57
|
};
|
|
40
58
|
type AdminClient = ReturnType<typeof createAdminClient>;
|
|
41
59
|
/**
|