@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
package/dist/resources/index.js
CHANGED
|
@@ -2355,6 +2355,166 @@ function alertRulesClient(t) {
|
|
|
2355
2355
|
};
|
|
2356
2356
|
}
|
|
2357
2357
|
|
|
2358
|
+
// src/resources/metrics.ts
|
|
2359
|
+
var BASE7 = "/api/admin/metrics";
|
|
2360
|
+
function metricsClient(t) {
|
|
2361
|
+
async function list() {
|
|
2362
|
+
return t.request("GET", BASE7);
|
|
2363
|
+
}
|
|
2364
|
+
async function resolve(idOrName) {
|
|
2365
|
+
const all = await list();
|
|
2366
|
+
const byId = all.find((m) => m.id === idOrName);
|
|
2367
|
+
if (byId) return byId;
|
|
2368
|
+
const byPrefix = all.filter((m) => m.id.startsWith(idOrName));
|
|
2369
|
+
if (byPrefix.length === 1) return byPrefix[0];
|
|
2370
|
+
if (byPrefix.length > 1) throw new ApiError(`Metric id prefix '${idOrName}' is ambiguous`, 400);
|
|
2371
|
+
const byName = all.filter((m) => m.name === idOrName);
|
|
2372
|
+
if (byName.length === 1) return byName[0];
|
|
2373
|
+
if (byName.length > 1)
|
|
2374
|
+
throw new ApiError(`Metric name '${idOrName}' is ambiguous \u2014 pass an id`, 400);
|
|
2375
|
+
throw new ApiError(`Metric '${idOrName}' not found`, 404);
|
|
2376
|
+
}
|
|
2377
|
+
return {
|
|
2378
|
+
list,
|
|
2379
|
+
resolve,
|
|
2380
|
+
get: (id) => t.request("GET", `${BASE7}/${id}`),
|
|
2381
|
+
create: (input) => t.request("POST", BASE7, input),
|
|
2382
|
+
delete: (id) => t.request("DELETE", `${BASE7}/${id}`)
|
|
2383
|
+
};
|
|
2384
|
+
}
|
|
2385
|
+
|
|
2386
|
+
// src/resources/events.ts
|
|
2387
|
+
var BASE8 = "/api/admin/events";
|
|
2388
|
+
function eventsClient(t) {
|
|
2389
|
+
async function list() {
|
|
2390
|
+
return t.request("GET", BASE8);
|
|
2391
|
+
}
|
|
2392
|
+
async function resolve(idOrName) {
|
|
2393
|
+
const all = await list();
|
|
2394
|
+
const byId = all.find((e) => e.id === idOrName);
|
|
2395
|
+
if (byId) return byId;
|
|
2396
|
+
const byPrefix = all.filter((e) => e.id.startsWith(idOrName));
|
|
2397
|
+
if (byPrefix.length === 1) return byPrefix[0];
|
|
2398
|
+
if (byPrefix.length > 1) throw new ApiError(`Event id prefix '${idOrName}' is ambiguous`, 400);
|
|
2399
|
+
const byName = all.filter((e) => e.name === idOrName);
|
|
2400
|
+
if (byName.length === 1) return byName[0];
|
|
2401
|
+
if (byName.length > 1)
|
|
2402
|
+
throw new ApiError(`Event name '${idOrName}' is ambiguous \u2014 pass an id`, 400);
|
|
2403
|
+
throw new ApiError(`Event '${idOrName}' not found`, 404);
|
|
2404
|
+
}
|
|
2405
|
+
return {
|
|
2406
|
+
list,
|
|
2407
|
+
resolve,
|
|
2408
|
+
get: (id) => t.request("GET", `${BASE8}/${id}`),
|
|
2409
|
+
create: (input) => t.request("POST", BASE8, input),
|
|
2410
|
+
update: (id, input) => t.request("PATCH", `${BASE8}/${id}`, input),
|
|
2411
|
+
approve: (id, input = {}) => t.request("POST", `${BASE8}/${id}/approve`, input),
|
|
2412
|
+
delete: (id) => t.request("DELETE", `${BASE8}/${id}`)
|
|
2413
|
+
};
|
|
2414
|
+
}
|
|
2415
|
+
|
|
2416
|
+
// src/resources/ops.ts
|
|
2417
|
+
var FEEDBACK = "/api/admin/feedback";
|
|
2418
|
+
var NOTIFY = "/api/admin/notifications";
|
|
2419
|
+
var CREATE_PATH = {
|
|
2420
|
+
bug: "/api/admin/bugs",
|
|
2421
|
+
feature_request: "/api/admin/feature-requests"
|
|
2422
|
+
};
|
|
2423
|
+
function itemPath(handle) {
|
|
2424
|
+
return `${FEEDBACK}/${encodeURIComponent(handle)}`;
|
|
2425
|
+
}
|
|
2426
|
+
function opsClient(t) {
|
|
2427
|
+
return {
|
|
2428
|
+
list: (query = {}) => {
|
|
2429
|
+
const q = {};
|
|
2430
|
+
if (query.type) q.type = query.type;
|
|
2431
|
+
if (query.status) q.status = query.status;
|
|
2432
|
+
if (query.limit !== void 0) q.limit = String(query.limit);
|
|
2433
|
+
return t.request("GET", FEEDBACK, void 0, q);
|
|
2434
|
+
},
|
|
2435
|
+
get: (handle) => t.request("GET", itemPath(handle)),
|
|
2436
|
+
create: ({ type, ...body }) => t.request("POST", CREATE_PATH[type], body),
|
|
2437
|
+
update: (handle, input) => t.request("PATCH", itemPath(handle), input),
|
|
2438
|
+
linkPr: (handle, input) => t.request("POST", `${itemPath(handle)}/link-pr`, input),
|
|
2439
|
+
notify: (input) => t.request("POST", NOTIFY, {
|
|
2440
|
+
title: input.title,
|
|
2441
|
+
summary: input.summary,
|
|
2442
|
+
steps: input.steps ?? [],
|
|
2443
|
+
...input.href ? { href: input.href } : {},
|
|
2444
|
+
...input.dedupeKey ? { dedupeKey: input.dedupeKey } : {}
|
|
2445
|
+
}),
|
|
2446
|
+
channels: () => t.request("GET", "/api/admin/slack/channels")
|
|
2447
|
+
};
|
|
2448
|
+
}
|
|
2449
|
+
|
|
2450
|
+
// src/resources/projects.ts
|
|
2451
|
+
var BASE9 = "/api/admin/projects";
|
|
2452
|
+
function projectsClient(t) {
|
|
2453
|
+
return {
|
|
2454
|
+
upsert: (input) => t.request("POST", `${BASE9}/upsert`, {
|
|
2455
|
+
domain: input.domain,
|
|
2456
|
+
...input.name ? { name: input.name } : {}
|
|
2457
|
+
}),
|
|
2458
|
+
current: () => t.request("GET", `${BASE9}/current`)
|
|
2459
|
+
};
|
|
2460
|
+
}
|
|
2461
|
+
|
|
2462
|
+
// src/resources/i18n.ts
|
|
2463
|
+
var PROFILES = "/api/admin/i18n/profiles";
|
|
2464
|
+
var KEYS = "/api/admin/i18n/keys";
|
|
2465
|
+
var DRAFTS = "/api/admin/i18n/drafts";
|
|
2466
|
+
function i18nClient(t) {
|
|
2467
|
+
async function listProfiles() {
|
|
2468
|
+
return t.request("GET", PROFILES);
|
|
2469
|
+
}
|
|
2470
|
+
async function resolveProfile(name) {
|
|
2471
|
+
const all = await listProfiles();
|
|
2472
|
+
const found = all.find((p) => p.name === name);
|
|
2473
|
+
if (!found)
|
|
2474
|
+
throw new ApiError(
|
|
2475
|
+
`Profile '${name}' not found. Existing: ${all.map((p) => p.name).join(", ") || "(none)"}`,
|
|
2476
|
+
404
|
|
2477
|
+
);
|
|
2478
|
+
return found;
|
|
2479
|
+
}
|
|
2480
|
+
async function listKeys(query = {}) {
|
|
2481
|
+
const q = {};
|
|
2482
|
+
if (query.profile_id) q.profile_id = query.profile_id;
|
|
2483
|
+
if (query.prefix) q.prefix = query.prefix;
|
|
2484
|
+
if (query.limit !== void 0) q.limit = String(query.limit);
|
|
2485
|
+
const res = await t.request("GET", KEYS, void 0, q);
|
|
2486
|
+
return Array.isArray(res) ? res : res.keys;
|
|
2487
|
+
}
|
|
2488
|
+
function updateKey(id, input) {
|
|
2489
|
+
return t.request("PUT", `${KEYS}/${id}`, input);
|
|
2490
|
+
}
|
|
2491
|
+
return {
|
|
2492
|
+
listProfiles,
|
|
2493
|
+
resolveProfile,
|
|
2494
|
+
listKeys,
|
|
2495
|
+
updateKey,
|
|
2496
|
+
createProfile: (input) => t.request("POST", PROFILES, input),
|
|
2497
|
+
pushKeys: (input) => t.request("POST", KEYS, input),
|
|
2498
|
+
updateKeyByName: async (profileId, key, input) => {
|
|
2499
|
+
const rows = await listKeys({ profile_id: profileId, prefix: key, limit: 500 });
|
|
2500
|
+
const match = rows.find((k) => k.key === key);
|
|
2501
|
+
if (!match)
|
|
2502
|
+
throw new ApiError(`Key '${key}' not found in profile. Add it first with i18n push.`, 404);
|
|
2503
|
+
return updateKey(match.id, input);
|
|
2504
|
+
},
|
|
2505
|
+
listDrafts: () => t.request("GET", DRAFTS),
|
|
2506
|
+
publish: (profileId, input) => t.request("POST", `${PROFILES}/${profileId}/publish`, { chunk: input.chunk ?? "default" })
|
|
2507
|
+
};
|
|
2508
|
+
}
|
|
2509
|
+
|
|
2510
|
+
// src/resources/attributes.ts
|
|
2511
|
+
var BASE10 = "/api/admin/attributes";
|
|
2512
|
+
function attributesClient(t) {
|
|
2513
|
+
return {
|
|
2514
|
+
list: () => t.request("GET", BASE10)
|
|
2515
|
+
};
|
|
2516
|
+
}
|
|
2517
|
+
|
|
2358
2518
|
// src/resources/index.ts
|
|
2359
2519
|
function createAdminClient(transport) {
|
|
2360
2520
|
return {
|
|
@@ -2364,7 +2524,13 @@ function createAdminClient(transport) {
|
|
|
2364
2524
|
configs: configsClient(transport),
|
|
2365
2525
|
universes: universesClient(transport),
|
|
2366
2526
|
killswitches: killswitchesClient(transport),
|
|
2367
|
-
alertRules: alertRulesClient(transport)
|
|
2527
|
+
alertRules: alertRulesClient(transport),
|
|
2528
|
+
metrics: metricsClient(transport),
|
|
2529
|
+
events: eventsClient(transport),
|
|
2530
|
+
ops: opsClient(transport),
|
|
2531
|
+
projects: projectsClient(transport),
|
|
2532
|
+
i18n: i18nClient(transport),
|
|
2533
|
+
attributes: attributesClient(transport)
|
|
2368
2534
|
};
|
|
2369
2535
|
}
|
|
2370
2536
|
var RESOURCE_REGISTRY = [
|
|
@@ -2377,15 +2543,21 @@ var RESOURCE_REGISTRY = [
|
|
|
2377
2543
|
export {
|
|
2378
2544
|
RESOURCE_REGISTRY,
|
|
2379
2545
|
alertRulesClient,
|
|
2546
|
+
attributesClient,
|
|
2380
2547
|
configsClient,
|
|
2381
2548
|
configsResource,
|
|
2382
2549
|
createAdminClient,
|
|
2550
|
+
eventsClient,
|
|
2383
2551
|
experimentsClient,
|
|
2384
2552
|
experimentsResource,
|
|
2385
2553
|
gatesClient,
|
|
2386
2554
|
gatesResource,
|
|
2555
|
+
i18nClient,
|
|
2387
2556
|
killswitchesClient,
|
|
2388
2557
|
killswitchesResource,
|
|
2558
|
+
metricsClient,
|
|
2559
|
+
opsClient,
|
|
2560
|
+
projectsClient,
|
|
2389
2561
|
universesClient,
|
|
2390
2562
|
universesResource
|
|
2391
2563
|
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Transport } from '../transport.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Metric definitions — the event-backed queries that power tracking dashboards
|
|
5
|
+
* and experiment success/guardrail metrics. A metric pins one event, one
|
|
6
|
+
* aggregation, and (for sum/avg) a value path, expressed as the metric query
|
|
7
|
+
* DSL or its typed IR form.
|
|
8
|
+
*
|
|
9
|
+
* The admin endpoint accepts either `query` (the DSL string) or `query_ir`
|
|
10
|
+
* (the typed IR). The CLI's vendored DSL parser turns `--query` into IR
|
|
11
|
+
* client-side for an early error; the server validates either form, so the
|
|
12
|
+
* registry op can pass `query` straight through and stay worker-safe (no
|
|
13
|
+
* parser bundled into the shared registry).
|
|
14
|
+
*/
|
|
15
|
+
interface Metric {
|
|
16
|
+
id: string;
|
|
17
|
+
name: string;
|
|
18
|
+
folder: string | null;
|
|
19
|
+
eventName: string;
|
|
20
|
+
aggregation: string;
|
|
21
|
+
valuePath: string | null;
|
|
22
|
+
queryIr?: unknown;
|
|
23
|
+
[key: string]: unknown;
|
|
24
|
+
}
|
|
25
|
+
interface MetricCreateInput {
|
|
26
|
+
name: string;
|
|
27
|
+
event_name: string;
|
|
28
|
+
folder?: string | null;
|
|
29
|
+
winsorize_pct?: number;
|
|
30
|
+
min_detectable_effect?: number | null;
|
|
31
|
+
/** Metric query DSL string, e.g. `sum(purchase, amount)`. */
|
|
32
|
+
query?: string;
|
|
33
|
+
/** Typed IR form (alternative to `query`). */
|
|
34
|
+
query_ir?: unknown;
|
|
35
|
+
}
|
|
36
|
+
interface MetricsClient {
|
|
37
|
+
/** Every metric in the project (the list endpoint is not paginated). */
|
|
38
|
+
list(): Promise<Metric[]>;
|
|
39
|
+
/** Fetch one metric by its full id. */
|
|
40
|
+
get(id: string): Promise<Metric>;
|
|
41
|
+
/** Resolve by exact id, unique id-prefix, or exact (unique) name. */
|
|
42
|
+
resolve(idOrName: string): Promise<Metric>;
|
|
43
|
+
create(input: MetricCreateInput): Promise<{
|
|
44
|
+
id: string;
|
|
45
|
+
name: string;
|
|
46
|
+
}>;
|
|
47
|
+
/** Soft-delete (the user-facing verb is `archive`). */
|
|
48
|
+
delete(id: string): Promise<{
|
|
49
|
+
ok: true;
|
|
50
|
+
}>;
|
|
51
|
+
}
|
|
52
|
+
declare function metricsClient(t: Transport): MetricsClient;
|
|
53
|
+
|
|
54
|
+
export { type Metric, type MetricCreateInput, type MetricsClient, metricsClient };
|
|
@@ -0,0 +1,40 @@
|
|
|
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/metrics.ts
|
|
12
|
+
var BASE = "/api/admin/metrics";
|
|
13
|
+
function metricsClient(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((m) => m.id === idOrName);
|
|
20
|
+
if (byId) return byId;
|
|
21
|
+
const byPrefix = all.filter((m) => m.id.startsWith(idOrName));
|
|
22
|
+
if (byPrefix.length === 1) return byPrefix[0];
|
|
23
|
+
if (byPrefix.length > 1) throw new ApiError(`Metric id prefix '${idOrName}' is ambiguous`, 400);
|
|
24
|
+
const byName = all.filter((m) => m.name === idOrName);
|
|
25
|
+
if (byName.length === 1) return byName[0];
|
|
26
|
+
if (byName.length > 1)
|
|
27
|
+
throw new ApiError(`Metric name '${idOrName}' is ambiguous \u2014 pass an id`, 400);
|
|
28
|
+
throw new ApiError(`Metric '${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
|
+
delete: (id) => t.request("DELETE", `${BASE}/${id}`)
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
export {
|
|
39
|
+
metricsClient
|
|
40
|
+
};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { Transport } from '../transport.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Operational queue — the unified feedback table of bug reports, feature
|
|
5
|
+
* requests, and auto-filed error/alert tickets, over `/api/admin/feedback`.
|
|
6
|
+
* One resource covers list/get/create/update/link-pr plus the `notify`
|
|
7
|
+
* escalation bell (`/api/admin/notifications`) and the Slack channel reader
|
|
8
|
+
* (`/api/admin/slack/channels`) used to resolve alert-rule targets.
|
|
9
|
+
*
|
|
10
|
+
* Item handles are either the per-project number (`7`) or the full id; the
|
|
11
|
+
* admin API resolves either, so callers pass the handle straight through.
|
|
12
|
+
*/
|
|
13
|
+
type OpsType = "bug" | "feature_request" | "error" | "alert";
|
|
14
|
+
type OpsStatus = "open" | "triaged" | "in_progress" | "ready_for_qa" | "resolved" | "wont_fix";
|
|
15
|
+
type OpsPriority = "nice_to_have" | "medium" | "high" | "critical";
|
|
16
|
+
interface OpsItem {
|
|
17
|
+
id: string;
|
|
18
|
+
number: number | null;
|
|
19
|
+
type: string;
|
|
20
|
+
title: string;
|
|
21
|
+
status: string;
|
|
22
|
+
priority: string | null;
|
|
23
|
+
sourceRef?: string | null;
|
|
24
|
+
createdAt: string;
|
|
25
|
+
[key: string]: unknown;
|
|
26
|
+
}
|
|
27
|
+
interface OpsListQuery {
|
|
28
|
+
type?: OpsType | "all";
|
|
29
|
+
status?: OpsStatus | "all";
|
|
30
|
+
limit?: number;
|
|
31
|
+
}
|
|
32
|
+
interface OpsCreateInput {
|
|
33
|
+
/** Only `bug` / `feature_request` are user-fileable; error/alert are auto-filed. */
|
|
34
|
+
type: "bug" | "feature_request";
|
|
35
|
+
title: string;
|
|
36
|
+
body?: string;
|
|
37
|
+
priority?: OpsPriority;
|
|
38
|
+
stepsToReproduce?: string;
|
|
39
|
+
pageUrl?: string;
|
|
40
|
+
}
|
|
41
|
+
interface OpsUpdateInput {
|
|
42
|
+
status?: OpsStatus;
|
|
43
|
+
priority?: OpsPriority;
|
|
44
|
+
}
|
|
45
|
+
interface OpsNotifyInput {
|
|
46
|
+
title: string;
|
|
47
|
+
summary: string;
|
|
48
|
+
steps?: string[];
|
|
49
|
+
href?: string;
|
|
50
|
+
dedupeKey?: string;
|
|
51
|
+
}
|
|
52
|
+
interface SlackChannel {
|
|
53
|
+
id: string;
|
|
54
|
+
name: string;
|
|
55
|
+
isPrivate?: boolean;
|
|
56
|
+
}
|
|
57
|
+
interface SlackChannelsResponse {
|
|
58
|
+
connected: boolean;
|
|
59
|
+
channels: SlackChannel[];
|
|
60
|
+
}
|
|
61
|
+
interface OpsClient {
|
|
62
|
+
list(query?: OpsListQuery): Promise<OpsItem[]>;
|
|
63
|
+
get(handle: string): Promise<OpsItem>;
|
|
64
|
+
/** File a bug or feature request. Fires project connectors (GitHub/Slack). */
|
|
65
|
+
create(input: OpsCreateInput): Promise<{
|
|
66
|
+
id: string;
|
|
67
|
+
number?: number | null;
|
|
68
|
+
}>;
|
|
69
|
+
update(handle: string, input: OpsUpdateInput): Promise<{
|
|
70
|
+
id: string;
|
|
71
|
+
}>;
|
|
72
|
+
linkPr(handle: string, input: {
|
|
73
|
+
prNumber: number | null;
|
|
74
|
+
prUrl?: string;
|
|
75
|
+
}): Promise<{
|
|
76
|
+
id: string;
|
|
77
|
+
}>;
|
|
78
|
+
/** Raise a 'needs your attention' bell notification (create-only, idempotent on dedupeKey). */
|
|
79
|
+
notify(input: OpsNotifyInput): Promise<{
|
|
80
|
+
dedupeKey: string;
|
|
81
|
+
dispatched: boolean;
|
|
82
|
+
}>;
|
|
83
|
+
/** The project's Slack channels — used to resolve alert-rule `--slack-channel`. */
|
|
84
|
+
channels(): Promise<SlackChannelsResponse>;
|
|
85
|
+
}
|
|
86
|
+
declare function opsClient(t: Transport): OpsClient;
|
|
87
|
+
|
|
88
|
+
export { type OpsClient, type OpsCreateInput, type OpsItem, type OpsListQuery, type OpsNotifyInput, type OpsPriority, type OpsStatus, type OpsType, type OpsUpdateInput, type SlackChannel, type SlackChannelsResponse, opsClient };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// src/resources/ops.ts
|
|
2
|
+
var FEEDBACK = "/api/admin/feedback";
|
|
3
|
+
var NOTIFY = "/api/admin/notifications";
|
|
4
|
+
var CREATE_PATH = {
|
|
5
|
+
bug: "/api/admin/bugs",
|
|
6
|
+
feature_request: "/api/admin/feature-requests"
|
|
7
|
+
};
|
|
8
|
+
function itemPath(handle) {
|
|
9
|
+
return `${FEEDBACK}/${encodeURIComponent(handle)}`;
|
|
10
|
+
}
|
|
11
|
+
function opsClient(t) {
|
|
12
|
+
return {
|
|
13
|
+
list: (query = {}) => {
|
|
14
|
+
const q = {};
|
|
15
|
+
if (query.type) q.type = query.type;
|
|
16
|
+
if (query.status) q.status = query.status;
|
|
17
|
+
if (query.limit !== void 0) q.limit = String(query.limit);
|
|
18
|
+
return t.request("GET", FEEDBACK, void 0, q);
|
|
19
|
+
},
|
|
20
|
+
get: (handle) => t.request("GET", itemPath(handle)),
|
|
21
|
+
create: ({ type, ...body }) => t.request("POST", CREATE_PATH[type], body),
|
|
22
|
+
update: (handle, input) => t.request("PATCH", itemPath(handle), input),
|
|
23
|
+
linkPr: (handle, input) => t.request("POST", `${itemPath(handle)}/link-pr`, input),
|
|
24
|
+
notify: (input) => t.request("POST", NOTIFY, {
|
|
25
|
+
title: input.title,
|
|
26
|
+
summary: input.summary,
|
|
27
|
+
steps: input.steps ?? [],
|
|
28
|
+
...input.href ? { href: input.href } : {},
|
|
29
|
+
...input.dedupeKey ? { dedupeKey: input.dedupeKey } : {}
|
|
30
|
+
}),
|
|
31
|
+
channels: () => t.request("GET", "/api/admin/slack/channels")
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export {
|
|
35
|
+
opsClient
|
|
36
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Transport } from '../transport.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Projects — find-or-create by domain (`upsert`) and the auth-resolved current
|
|
5
|
+
* project (`current`). The `.shipeasy` write that `bind`/`upsert --bind`
|
|
6
|
+
* performs is a CONSUMER side-effect layered on top of these calls (it touches
|
|
7
|
+
* the filesystem); it never lives in the resource, so this stays worker-safe.
|
|
8
|
+
*/
|
|
9
|
+
interface UpsertResult {
|
|
10
|
+
id: string;
|
|
11
|
+
name: string;
|
|
12
|
+
domain: string | null;
|
|
13
|
+
owner_email: string;
|
|
14
|
+
created: boolean;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* The project the caller's auth header resolves to. The server reads the
|
|
18
|
+
* project from the credential — there is no id parameter — so `current` works
|
|
19
|
+
* with nothing but the session, which is what makes a registry-driven `whoami`
|
|
20
|
+
* possible.
|
|
21
|
+
*/
|
|
22
|
+
interface CurrentProject {
|
|
23
|
+
id: string;
|
|
24
|
+
name: string;
|
|
25
|
+
domain: string | null;
|
|
26
|
+
ownerEmail: string;
|
|
27
|
+
plan: "free" | "paid";
|
|
28
|
+
status: "active" | "inactive";
|
|
29
|
+
subscriptionStatus: string;
|
|
30
|
+
billingInterval: "monthly" | "annual";
|
|
31
|
+
currentPeriodEnd: string | null;
|
|
32
|
+
trialEndsAt: string | null;
|
|
33
|
+
cancelAtPeriodEnd: number;
|
|
34
|
+
moduleTranslations: boolean | number;
|
|
35
|
+
moduleConfigs: boolean | number;
|
|
36
|
+
moduleGates: boolean | number;
|
|
37
|
+
moduleExperiments: boolean | number;
|
|
38
|
+
moduleFeedback: boolean | number;
|
|
39
|
+
createdAt: string;
|
|
40
|
+
updatedAt: string;
|
|
41
|
+
[key: string]: unknown;
|
|
42
|
+
}
|
|
43
|
+
interface ProjectsClient {
|
|
44
|
+
/** Find-or-create a project by domain (idempotent). */
|
|
45
|
+
upsert(input: {
|
|
46
|
+
domain: string;
|
|
47
|
+
name?: string;
|
|
48
|
+
}): Promise<UpsertResult>;
|
|
49
|
+
/** The project the auth header resolves to (no id needed). */
|
|
50
|
+
current(): Promise<CurrentProject>;
|
|
51
|
+
}
|
|
52
|
+
declare function projectsClient(t: Transport): ProjectsClient;
|
|
53
|
+
|
|
54
|
+
export { type CurrentProject, type ProjectsClient, type UpsertResult, projectsClient };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// src/resources/projects.ts
|
|
2
|
+
var BASE = "/api/admin/projects";
|
|
3
|
+
function projectsClient(t) {
|
|
4
|
+
return {
|
|
5
|
+
upsert: (input) => t.request("POST", `${BASE}/upsert`, {
|
|
6
|
+
domain: input.domain,
|
|
7
|
+
...input.name ? { name: input.name } : {}
|
|
8
|
+
}),
|
|
9
|
+
current: () => t.request("GET", `${BASE}/current`)
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
export {
|
|
13
|
+
projectsClient
|
|
14
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shipeasy/openapi",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"description": "Shipeasy admin OpenAPI spec + typed client. Single source of truth for endpoint paths, request/response shapes, and the resource registry consumed by @shipeasy/cli and @shipeasy/mcp.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -26,6 +26,10 @@
|
|
|
26
26
|
"types": "./dist/schemas/*.d.ts",
|
|
27
27
|
"default": "./dist/schemas/*.js"
|
|
28
28
|
},
|
|
29
|
+
"./operations": {
|
|
30
|
+
"types": "./dist/operations/index.d.ts",
|
|
31
|
+
"default": "./dist/operations/index.js"
|
|
32
|
+
},
|
|
29
33
|
"./transport": {
|
|
30
34
|
"types": "./dist/transport.d.ts",
|
|
31
35
|
"default": "./dist/transport.js"
|
|
@@ -33,6 +37,10 @@
|
|
|
33
37
|
"./pagination": {
|
|
34
38
|
"types": "./dist/pagination.d.ts",
|
|
35
39
|
"default": "./dist/pagination.js"
|
|
40
|
+
},
|
|
41
|
+
"./node-context": {
|
|
42
|
+
"types": "./dist/node-context.d.ts",
|
|
43
|
+
"default": "./dist/node-context.js"
|
|
36
44
|
}
|
|
37
45
|
},
|
|
38
46
|
"files": [
|
|
@@ -49,6 +57,7 @@
|
|
|
49
57
|
"zod": "^4.0.0"
|
|
50
58
|
},
|
|
51
59
|
"devDependencies": {
|
|
60
|
+
"@types/node": "^20.19.39",
|
|
52
61
|
"openapi-types": "^12.1.3",
|
|
53
62
|
"tsup": "^8.3.0",
|
|
54
63
|
"tsx": "^4.21.0",
|