@dichovsky/testrail-api-client 1.0.0 → 2.1.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 (137) hide show
  1. package/README.md +22 -0
  2. package/dist/cli/auth.d.ts +21 -0
  3. package/dist/cli/auth.js +16 -0
  4. package/dist/cli/body.d.ts +42 -0
  5. package/dist/cli/body.js +89 -0
  6. package/dist/cli/dispatch.d.ts +16 -0
  7. package/dist/cli/dispatch.js +87 -0
  8. package/dist/cli/handler-context.d.ts +43 -0
  9. package/dist/cli/handler-context.js +2 -0
  10. package/dist/cli/handlers/case-write.d.ts +4 -0
  11. package/dist/cli/handlers/case-write.js +26 -0
  12. package/dist/cli/handlers/case.d.ts +4 -0
  13. package/dist/cli/handlers/case.js +11 -0
  14. package/dist/cli/handlers/milestone.d.ts +4 -0
  15. package/dist/cli/handlers/milestone.js +15 -0
  16. package/dist/cli/handlers/project.d.ts +4 -0
  17. package/dist/cli/handlers/project.js +11 -0
  18. package/dist/cli/handlers/result-write.d.ts +4 -0
  19. package/dist/cli/handlers/result-write.js +40 -0
  20. package/dist/cli/handlers/result.d.ts +3 -0
  21. package/dist/cli/handlers/result.js +11 -0
  22. package/dist/cli/handlers/run-write.d.ts +10 -0
  23. package/dist/cli/handlers/run-write.js +29 -0
  24. package/dist/cli/handlers/run.d.ts +4 -0
  25. package/dist/cli/handlers/run.js +15 -0
  26. package/dist/cli/handlers/suite.d.ts +4 -0
  27. package/dist/cli/handlers/suite.js +10 -0
  28. package/dist/cli/handlers/user.d.ts +4 -0
  29. package/dist/cli/handlers/user.js +11 -0
  30. package/dist/cli/ids.d.ts +6 -0
  31. package/dist/cli/ids.js +20 -0
  32. package/dist/cli/index.d.ts +3 -0
  33. package/dist/cli/index.js +198 -0
  34. package/dist/cli/install-skill.d.ts +35 -0
  35. package/dist/cli/install-skill.js +71 -0
  36. package/dist/cli/metadata.d.ts +37 -0
  37. package/dist/cli/metadata.js +151 -0
  38. package/dist/cli/output.d.ts +28 -0
  39. package/dist/cli/output.js +84 -0
  40. package/dist/cli.d.ts +1 -1
  41. package/dist/cli.js +1 -266
  42. package/dist/client-core.d.ts +16 -7
  43. package/dist/client-core.js +153 -27
  44. package/dist/client.d.ts +274 -118
  45. package/dist/client.js +404 -463
  46. package/dist/constants.d.ts +1 -0
  47. package/dist/constants.js +1 -0
  48. package/dist/errors.d.ts +11 -9
  49. package/dist/errors.js +12 -8
  50. package/dist/index.d.ts +4 -2
  51. package/dist/index.js +2 -1
  52. package/dist/modules/attachments.d.ts +19 -0
  53. package/dist/modules/attachments.js +64 -0
  54. package/dist/modules/cases.d.ts +13 -0
  55. package/dist/modules/cases.js +58 -0
  56. package/dist/modules/configurations.d.ts +14 -0
  57. package/dist/modules/configurations.js +37 -0
  58. package/dist/modules/datasets.d.ts +12 -0
  59. package/dist/modules/datasets.js +28 -0
  60. package/dist/modules/metadata.d.ts +14 -0
  61. package/dist/modules/metadata.js +31 -0
  62. package/dist/modules/milestones.d.ts +12 -0
  63. package/dist/modules/milestones.js +36 -0
  64. package/dist/modules/plans.d.ts +16 -0
  65. package/dist/modules/plans.js +59 -0
  66. package/dist/modules/projects.d.ts +36 -0
  67. package/dist/modules/projects.js +55 -0
  68. package/dist/modules/reports.d.ts +9 -0
  69. package/dist/modules/reports.js +16 -0
  70. package/dist/modules/results.d.ts +14 -0
  71. package/dist/modules/results.js +69 -0
  72. package/dist/modules/runs.d.ts +14 -0
  73. package/dist/modules/runs.js +57 -0
  74. package/dist/modules/sections.d.ts +16 -0
  75. package/dist/modules/sections.js +37 -0
  76. package/dist/modules/sharedSteps.d.ts +12 -0
  77. package/dist/modules/sharedSteps.js +28 -0
  78. package/dist/modules/suites.d.ts +37 -0
  79. package/dist/modules/suites.js +54 -0
  80. package/dist/modules/tests.d.ts +9 -0
  81. package/dist/modules/tests.js +25 -0
  82. package/dist/modules/users.d.ts +18 -0
  83. package/dist/modules/users.js +62 -0
  84. package/dist/modules/variables.d.ts +11 -0
  85. package/dist/modules/variables.js +24 -0
  86. package/dist/schemas.d.ts +544 -0
  87. package/dist/schemas.js +419 -0
  88. package/dist/types.d.ts +1 -55
  89. package/dist/utils.d.ts +2 -0
  90. package/dist/utils.js +4 -0
  91. package/package.json +23 -15
  92. package/skill/SKILL.md +395 -0
  93. package/src/cli/auth.ts +37 -0
  94. package/src/cli/body.ts +100 -0
  95. package/src/cli/dispatch.ts +91 -0
  96. package/src/cli/handler-context.ts +46 -0
  97. package/src/cli/handlers/case-write.ts +26 -0
  98. package/src/cli/handlers/case.ts +13 -0
  99. package/src/cli/handlers/milestone.ts +19 -0
  100. package/src/cli/handlers/project.ts +13 -0
  101. package/src/cli/handlers/result-write.ts +40 -0
  102. package/src/cli/handlers/result.ts +14 -0
  103. package/src/cli/handlers/run-write.ts +30 -0
  104. package/src/cli/handlers/run.ts +19 -0
  105. package/src/cli/handlers/suite.ts +12 -0
  106. package/src/cli/handlers/user.ts +13 -0
  107. package/src/cli/ids.ts +20 -0
  108. package/src/cli/index.ts +224 -0
  109. package/src/cli/install-skill.ts +89 -0
  110. package/src/cli/metadata.ts +194 -0
  111. package/src/cli/output.ts +96 -0
  112. package/src/cli.ts +1 -286
  113. package/src/client-core.ts +183 -67
  114. package/src/client.ts +414 -483
  115. package/src/constants.ts +1 -0
  116. package/src/errors.ts +18 -11
  117. package/src/index.ts +50 -8
  118. package/src/modules/attachments.ts +125 -0
  119. package/src/modules/cases.ts +78 -0
  120. package/src/modules/configurations.ts +68 -0
  121. package/src/modules/datasets.ts +44 -0
  122. package/src/modules/metadata.ts +63 -0
  123. package/src/modules/milestones.ts +54 -0
  124. package/src/modules/plans.ts +89 -0
  125. package/src/modules/projects.ts +67 -0
  126. package/src/modules/reports.ts +23 -0
  127. package/src/modules/results.ts +90 -0
  128. package/src/modules/runs.ts +70 -0
  129. package/src/modules/sections.ts +55 -0
  130. package/src/modules/sharedSteps.ts +44 -0
  131. package/src/modules/suites.ts +67 -0
  132. package/src/modules/tests.ts +28 -0
  133. package/src/modules/users.ts +87 -0
  134. package/src/modules/variables.ts +36 -0
  135. package/src/schemas.ts +551 -0
  136. package/src/types.ts +11 -60
  137. package/src/utils.ts +5 -0
package/README.md CHANGED
@@ -55,6 +55,26 @@ A comprehensive, type-safe TypeScript/JavaScript client for the [TestRail API](h
55
55
  npm install @dichovsky/testrail-api-client
56
56
  ```
57
57
 
58
+ ## Using with AI Coding Agents
59
+
60
+ This package ships a Claude Code skill at `skill/SKILL.md` that teaches coding agents how to use the bundled `testrail` CLI. Install it into your project (or globally) so Claude Code auto-loads it whenever you ask the agent to query or write TestRail entities.
61
+
62
+ ```bash
63
+ # Install into the current project: ./.claude/skills/testrail-cli/SKILL.md
64
+ npx testrail install-skill
65
+
66
+ # Or globally: ~/.claude/skills/testrail-cli/SKILL.md
67
+ npx testrail install-skill --global
68
+
69
+ # Overwrite an existing install
70
+ npx testrail install-skill --force
71
+
72
+ # Just print the bundled SKILL.md path (for scripting / vendoring)
73
+ npx testrail install-skill --print-path
74
+ ```
75
+
76
+ The skill description triggers auto-load when an agent's prompt mentions TestRail entities (projects, suites, cases, runs, results, milestones, users) or when `TESTRAIL_BASE_URL` / `TESTRAIL_EMAIL` / `TESTRAIL_API_KEY` are set in the environment. The bundled CLI itself supports both read (`get`, `list`) and write (`add`, `update`, `add-bulk`, `close`) operations — see `skill/SKILL.md` for the complete command surface and recipes.
77
+
58
78
  ## Quick Start
59
79
 
60
80
  ```typescript
@@ -356,6 +376,8 @@ const priorities = await client.getPriorities();
356
376
  const milestones = await client.getMilestones(projectId);
357
377
  ```
358
378
 
379
+ Advanced usage: the client also exposes domain modules such as `client.metadata` for organization, but the supported/documented usage pattern remains the top-level `client.getX()` methods.
380
+
359
381
  ### Caching
360
382
 
361
383
  The client automatically caches GET requests to improve performance:
@@ -0,0 +1,21 @@
1
+ import type { TestRailConfig } from '../types.js';
2
+ export interface AuthFlags {
3
+ baseUrl: string | undefined;
4
+ email: string | undefined;
5
+ apiKey: string | undefined;
6
+ }
7
+ export interface AuthEnv {
8
+ TESTRAIL_BASE_URL?: string;
9
+ TESTRAIL_EMAIL?: string;
10
+ TESTRAIL_API_KEY?: string;
11
+ }
12
+ export type AuthResolution = {
13
+ ok: true;
14
+ config: TestRailConfig;
15
+ } | {
16
+ ok: false;
17
+ error: string;
18
+ };
19
+ export declare const MISSING_AUTH_MESSAGE = "Missing auth. Set TESTRAIL_BASE_URL, TESTRAIL_EMAIL, TESTRAIL_API_KEY or use --base-url, --email, --api-key flags.";
20
+ export declare function resolveAuth(flags: AuthFlags, env: AuthEnv): AuthResolution;
21
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1,16 @@
1
+ export const MISSING_AUTH_MESSAGE = 'Missing auth. Set TESTRAIL_BASE_URL, TESTRAIL_EMAIL, TESTRAIL_API_KEY or use --base-url, --email, --api-key flags.';
2
+ export function resolveAuth(flags, env) {
3
+ const baseUrl = flags.baseUrl ?? env.TESTRAIL_BASE_URL;
4
+ const email = flags.email ?? env.TESTRAIL_EMAIL;
5
+ const apiKey = flags.apiKey ?? env.TESTRAIL_API_KEY;
6
+ if (baseUrl === undefined ||
7
+ baseUrl === '' ||
8
+ email === undefined ||
9
+ email === '' ||
10
+ apiKey === undefined ||
11
+ apiKey === '') {
12
+ return { ok: false, error: MISSING_AUTH_MESSAGE };
13
+ }
14
+ return { ok: true, config: { baseUrl, email, apiKey } };
15
+ }
16
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1,42 @@
1
+ import type { z } from 'zod';
2
+ import type { BodyInput } from './handler-context.js';
3
+ /**
4
+ * Source of a parsed body. Reported alongside the resolution result so
5
+ * handlers (or callers writing recipes) can log which input mechanism the
6
+ * agent actually used.
7
+ */
8
+ export type BodySource = 'data' | 'file' | 'stdin';
9
+ export type BodyResolution<T> = {
10
+ ok: true;
11
+ payload: T;
12
+ source: BodySource;
13
+ } | {
14
+ ok: false;
15
+ error: string;
16
+ };
17
+ /**
18
+ * Resolve a write-action body from one of three mutually-exclusive sources:
19
+ *
20
+ * - `--data <json-string>` (provided via `BodyInput.dataFlag`)
21
+ * - `--data-file <path>` (provided via `BodyInput.dataFileFlag`; read via
22
+ * readFileSync so failures surface as a structured `ok: false` rather than
23
+ * crashing the CLI)
24
+ * - stdin (provided via `BodyInput.readStdin` thunk; the caller is
25
+ * responsible for non-TTY detection — only set the thunk when stdin is
26
+ * piped. The resolver invokes the thunk *only* when stdin is the
27
+ * selected source, so read actions and no-body writes never drain it.)
28
+ *
29
+ * After source resolution: JSON-parse the raw string, then validate against
30
+ * the supplied Zod schema. No coercion is applied (Q8 lock from
31
+ * SKILL-PLAN.md) — wrong types are rejected immediately so agent payload
32
+ * bugs surface at the CLI boundary rather than the API call site.
33
+ *
34
+ * Typed via `S extends z.ZodTypeAny` so the inferred payload type carries
35
+ * through to the caller — `resolveBody(input, AddCasePayloadSchema)` returns
36
+ * `BodyResolution<AddCasePayload>`, not `BodyResolution<unknown>`.
37
+ *
38
+ * @param input raw inputs from the CLI argv + stdin reader
39
+ * @param schema Zod schema for the expected payload shape
40
+ */
41
+ export declare function resolveBody<S extends z.ZodTypeAny>(input: BodyInput, schema: S): BodyResolution<z.infer<S>>;
42
+ //# sourceMappingURL=body.d.ts.map
@@ -0,0 +1,89 @@
1
+ import { readFileSync } from 'node:fs';
2
+ /**
3
+ * Resolve a write-action body from one of three mutually-exclusive sources:
4
+ *
5
+ * - `--data <json-string>` (provided via `BodyInput.dataFlag`)
6
+ * - `--data-file <path>` (provided via `BodyInput.dataFileFlag`; read via
7
+ * readFileSync so failures surface as a structured `ok: false` rather than
8
+ * crashing the CLI)
9
+ * - stdin (provided via `BodyInput.readStdin` thunk; the caller is
10
+ * responsible for non-TTY detection — only set the thunk when stdin is
11
+ * piped. The resolver invokes the thunk *only* when stdin is the
12
+ * selected source, so read actions and no-body writes never drain it.)
13
+ *
14
+ * After source resolution: JSON-parse the raw string, then validate against
15
+ * the supplied Zod schema. No coercion is applied (Q8 lock from
16
+ * SKILL-PLAN.md) — wrong types are rejected immediately so agent payload
17
+ * bugs surface at the CLI boundary rather than the API call site.
18
+ *
19
+ * Typed via `S extends z.ZodTypeAny` so the inferred payload type carries
20
+ * through to the caller — `resolveBody(input, AddCasePayloadSchema)` returns
21
+ * `BodyResolution<AddCasePayload>`, not `BodyResolution<unknown>`.
22
+ *
23
+ * @param input raw inputs from the CLI argv + stdin reader
24
+ * @param schema Zod schema for the expected payload shape
25
+ */
26
+ export function resolveBody(input, schema) {
27
+ const sources = [
28
+ input.dataFlag !== undefined ? 'data' : null,
29
+ input.dataFileFlag !== undefined ? 'file' : null,
30
+ input.readStdin !== undefined ? 'stdin' : null,
31
+ ].filter((s) => s !== null);
32
+ if (sources.length === 0) {
33
+ return {
34
+ ok: false,
35
+ error: 'Body required. Pass a JSON payload via --data <json>, --data-file <path>, or stdin pipe.',
36
+ };
37
+ }
38
+ if (sources.length > 1) {
39
+ return {
40
+ ok: false,
41
+ error: `Multiple body sources provided (${sources.join(', ')}). Use exactly one of --data, --data-file, or stdin.`,
42
+ };
43
+ }
44
+ let raw;
45
+ let source;
46
+ if (input.dataFlag !== undefined) {
47
+ raw = input.dataFlag;
48
+ source = 'data';
49
+ }
50
+ else if (input.dataFileFlag !== undefined) {
51
+ try {
52
+ raw = readFileSync(input.dataFileFlag, 'utf-8');
53
+ }
54
+ catch (e) {
55
+ return {
56
+ ok: false,
57
+ error: `Cannot read --data-file '${input.dataFileFlag}': ${e instanceof Error ? e.message : String(e)}`,
58
+ };
59
+ }
60
+ source = 'file';
61
+ }
62
+ else {
63
+ // stdin is the only remaining source. Invoke the thunk now (and only
64
+ // now) so the underlying readFileSync(0) call happens lazily.
65
+ try {
66
+ raw = input.readStdin();
67
+ }
68
+ catch (e) {
69
+ return {
70
+ ok: false,
71
+ error: `Cannot read stdin: ${e instanceof Error ? e.message : String(e)}`,
72
+ };
73
+ }
74
+ source = 'stdin';
75
+ }
76
+ let parsed;
77
+ try {
78
+ parsed = JSON.parse(raw);
79
+ }
80
+ catch (e) {
81
+ return { ok: false, error: `Invalid JSON: ${e instanceof Error ? e.message : String(e)}` };
82
+ }
83
+ const result = schema.safeParse(parsed);
84
+ if (!result.success) {
85
+ return { ok: false, error: `Payload validation failed: ${result.error.message}` };
86
+ }
87
+ return { ok: true, payload: result.data, source };
88
+ }
89
+ //# sourceMappingURL=body.js.map
@@ -0,0 +1,16 @@
1
+ import type { Handler } from './handler-context.js';
2
+ export type DispatchResult = {
3
+ ok: true;
4
+ handler: Handler;
5
+ } | {
6
+ ok: false;
7
+ error: string;
8
+ };
9
+ /**
10
+ * Returns every registered `resource:action` key. Used by the
11
+ * metadata↔dispatch consistency tests to catch handlers added without a
12
+ * matching metadata entry — the inverse of the metadata-first check.
13
+ */
14
+ export declare function getRegisteredActions(): readonly string[];
15
+ export declare function dispatch(resource: string, action: string): DispatchResult;
16
+ //# sourceMappingURL=dispatch.d.ts.map
@@ -0,0 +1,87 @@
1
+ import { handleProjectGet, handleProjectList } from './handlers/project.js';
2
+ import { handleSuiteGet, handleSuiteList } from './handlers/suite.js';
3
+ import { handleCaseGet, handleCaseList } from './handlers/case.js';
4
+ import { handleCaseAdd, handleCaseUpdate } from './handlers/case-write.js';
5
+ import { handleRunGet, handleRunList } from './handlers/run.js';
6
+ import { handleRunAdd, handleRunClose } from './handlers/run-write.js';
7
+ import { handleResultList } from './handlers/result.js';
8
+ import { handleResultAdd, handleResultAddBulk } from './handlers/result-write.js';
9
+ import { handleMilestoneGet, handleMilestoneList } from './handlers/milestone.js';
10
+ import { handleUserGet, handleUserList } from './handlers/user.js';
11
+ /**
12
+ * Single source of truth: every supported resource:action mapped to its handler.
13
+ * Adding an action is a one-line change here — no parallel registry to keep in
14
+ * sync. `dispatch()` derives both the known-resources list and the
15
+ * known-actions-per-resource list from this map's keys, so error wording stays
16
+ * accurate even as the map evolves.
17
+ *
18
+ * Keys are inserted in the canonical display order (`get` before `list`, etc.);
19
+ * JavaScript object iteration order is insertion-order-preserving for string
20
+ * keys, which keeps error messages stable.
21
+ */
22
+ const HANDLERS = {
23
+ 'project:get': handleProjectGet,
24
+ 'project:list': handleProjectList,
25
+ 'suite:get': handleSuiteGet,
26
+ 'suite:list': handleSuiteList,
27
+ 'case:get': handleCaseGet,
28
+ 'case:list': handleCaseList,
29
+ 'case:add': handleCaseAdd,
30
+ 'case:update': handleCaseUpdate,
31
+ 'run:get': handleRunGet,
32
+ 'run:list': handleRunList,
33
+ 'run:add': handleRunAdd,
34
+ 'run:close': handleRunClose,
35
+ 'result:list': handleResultList,
36
+ 'result:add': handleResultAdd,
37
+ 'result:add-bulk': handleResultAddBulk,
38
+ 'milestone:get': handleMilestoneGet,
39
+ 'milestone:list': handleMilestoneList,
40
+ 'user:get': handleUserGet,
41
+ 'user:list': handleUserList,
42
+ };
43
+ const RESOURCES = (() => {
44
+ const grouped = {};
45
+ for (const key of Object.keys(HANDLERS)) {
46
+ const [resource, action] = key.split(':');
47
+ if (resource === undefined || action === undefined)
48
+ continue;
49
+ const existing = grouped[resource];
50
+ if (existing === undefined) {
51
+ grouped[resource] = [action];
52
+ }
53
+ else {
54
+ existing.push(action);
55
+ }
56
+ }
57
+ return grouped;
58
+ })();
59
+ /**
60
+ * Returns every registered `resource:action` key. Used by the
61
+ * metadata↔dispatch consistency tests to catch handlers added without a
62
+ * matching metadata entry — the inverse of the metadata-first check.
63
+ */
64
+ export function getRegisteredActions() {
65
+ return Object.keys(HANDLERS);
66
+ }
67
+ export function dispatch(resource, action) {
68
+ const actions = RESOURCES[resource];
69
+ if (actions === undefined) {
70
+ return {
71
+ ok: false,
72
+ error: `Unknown resource '${resource}'. Use: ${Object.keys(RESOURCES).join(', ')}`,
73
+ };
74
+ }
75
+ if (!actions.includes(action)) {
76
+ return {
77
+ ok: false,
78
+ error: `Unknown action '${action}' for ${resource}. Use: ${actions.join(', ')}`,
79
+ };
80
+ }
81
+ const handler = HANDLERS[`${resource}:${action}`];
82
+ if (handler === undefined) {
83
+ return { ok: false, error: `No handler registered for ${resource}:${action}` };
84
+ }
85
+ return { ok: true, handler };
86
+ }
87
+ //# sourceMappingURL=dispatch.js.map
@@ -0,0 +1,43 @@
1
+ import type { TestRailClient } from '../client.js';
2
+ /**
3
+ * Parsed CLI argument bundle passed to every handler.
4
+ *
5
+ * `pathParams` is the slice of positional args after `[resource, action]` —
6
+ * read handlers consume `pathParams[0]` (the single id), write handlers may
7
+ * consume multiple (e.g., `result add <run_id> <case_id>` uses [0] and [1]).
8
+ */
9
+ export interface HandlerArgs {
10
+ pathParams: readonly string[];
11
+ projectId?: string;
12
+ suiteId?: string;
13
+ runId?: string;
14
+ caseId?: string;
15
+ limit?: string;
16
+ offset?: string;
17
+ }
18
+ /**
19
+ * Raw inputs for the body-source resolver. The handler decides whether to
20
+ * consume any of these (write handlers do; read handlers ignore). When all
21
+ * three are absent for a write action, the resolver emits a "body required"
22
+ * error.
23
+ *
24
+ * `readStdin` is a thunk rather than pre-read contents so stdin is *only*
25
+ * drained when the resolver actually selects it as the body source. Read
26
+ * actions and no-body writes (`run close`) never invoke it — avoiding the
27
+ * "tail -f | testrail run close" hang and the cost of slurping a large
28
+ * redirected stdin that the handler will throw away.
29
+ */
30
+ export interface BodyInput {
31
+ dataFlag?: string;
32
+ dataFileFlag?: string;
33
+ readStdin?: () => string;
34
+ }
35
+ export interface HandlerContext {
36
+ client: TestRailClient;
37
+ args: HandlerArgs;
38
+ bodyInput: BodyInput;
39
+ dryRun: boolean;
40
+ out: (data: unknown) => void;
41
+ }
42
+ export type Handler = (ctx: HandlerContext) => Promise<void>;
43
+ //# sourceMappingURL=handler-context.d.ts.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=handler-context.js.map
@@ -0,0 +1,4 @@
1
+ import type { HandlerContext } from '../handler-context.js';
2
+ export declare function handleCaseAdd(ctx: HandlerContext): Promise<void>;
3
+ export declare function handleCaseUpdate(ctx: HandlerContext): Promise<void>;
4
+ //# sourceMappingURL=case-write.d.ts.map
@@ -0,0 +1,26 @@
1
+ import { parseId } from '../ids.js';
2
+ import { resolveBody } from '../body.js';
3
+ import { AddCasePayloadSchema, UpdateCasePayloadSchema } from '../../schemas.js';
4
+ export async function handleCaseAdd(ctx) {
5
+ const sectionId = parseId(ctx.args.pathParams[0], 'section_id');
6
+ const body = resolveBody(ctx.bodyInput, AddCasePayloadSchema);
7
+ if (!body.ok)
8
+ throw new Error(body.error);
9
+ if (ctx.dryRun) {
10
+ ctx.out({ dryRun: true, action: 'case add', sectionId, payload: body.payload, source: body.source });
11
+ return;
12
+ }
13
+ ctx.out(await ctx.client.addCase(sectionId, body.payload));
14
+ }
15
+ export async function handleCaseUpdate(ctx) {
16
+ const caseId = parseId(ctx.args.pathParams[0], 'case_id');
17
+ const body = resolveBody(ctx.bodyInput, UpdateCasePayloadSchema);
18
+ if (!body.ok)
19
+ throw new Error(body.error);
20
+ if (ctx.dryRun) {
21
+ ctx.out({ dryRun: true, action: 'case update', caseId, payload: body.payload, source: body.source });
22
+ return;
23
+ }
24
+ ctx.out(await ctx.client.updateCase(caseId, body.payload));
25
+ }
26
+ //# sourceMappingURL=case-write.js.map
@@ -0,0 +1,4 @@
1
+ import type { HandlerContext } from '../handler-context.js';
2
+ export declare function handleCaseGet(ctx: HandlerContext): Promise<void>;
3
+ export declare function handleCaseList(ctx: HandlerContext): Promise<void>;
4
+ //# sourceMappingURL=case.d.ts.map
@@ -0,0 +1,11 @@
1
+ import { parseId, optInt } from '../ids.js';
2
+ export async function handleCaseGet(ctx) {
3
+ const id = parseId(ctx.args.pathParams[0], 'case id');
4
+ ctx.out(await ctx.client.getCase(id));
5
+ }
6
+ export async function handleCaseList(ctx) {
7
+ const pid = parseId(ctx.args.projectId, '--project-id');
8
+ const suiteId = optInt(ctx.args.suiteId);
9
+ ctx.out(await ctx.client.getCases(pid, suiteId !== undefined ? { suiteId } : undefined));
10
+ }
11
+ //# sourceMappingURL=case.js.map
@@ -0,0 +1,4 @@
1
+ import type { HandlerContext } from '../handler-context.js';
2
+ export declare function handleMilestoneGet(ctx: HandlerContext): Promise<void>;
3
+ export declare function handleMilestoneList(ctx: HandlerContext): Promise<void>;
4
+ //# sourceMappingURL=milestone.d.ts.map
@@ -0,0 +1,15 @@
1
+ import { parseId, optInt } from '../ids.js';
2
+ export async function handleMilestoneGet(ctx) {
3
+ const id = parseId(ctx.args.pathParams[0], 'milestone id');
4
+ ctx.out(await ctx.client.getMilestone(id));
5
+ }
6
+ export async function handleMilestoneList(ctx) {
7
+ const pid = parseId(ctx.args.projectId, '--project-id');
8
+ const limit = optInt(ctx.args.limit);
9
+ const offset = optInt(ctx.args.offset);
10
+ ctx.out(await ctx.client.getMilestones(pid, {
11
+ ...(limit !== undefined && { limit }),
12
+ ...(offset !== undefined && { offset }),
13
+ }));
14
+ }
15
+ //# sourceMappingURL=milestone.js.map
@@ -0,0 +1,4 @@
1
+ import type { HandlerContext } from '../handler-context.js';
2
+ export declare function handleProjectGet(ctx: HandlerContext): Promise<void>;
3
+ export declare function handleProjectList(ctx: HandlerContext): Promise<void>;
4
+ //# sourceMappingURL=project.d.ts.map
@@ -0,0 +1,11 @@
1
+ import { parseId, optInt } from '../ids.js';
2
+ export async function handleProjectGet(ctx) {
3
+ const id = parseId(ctx.args.pathParams[0], 'project id');
4
+ ctx.out(await ctx.client.getProject(id));
5
+ }
6
+ export async function handleProjectList(ctx) {
7
+ const limit = optInt(ctx.args.limit);
8
+ const offset = optInt(ctx.args.offset);
9
+ ctx.out(await ctx.client.getProjects(limit, offset));
10
+ }
11
+ //# sourceMappingURL=project.js.map
@@ -0,0 +1,4 @@
1
+ import type { HandlerContext } from '../handler-context.js';
2
+ export declare function handleResultAdd(ctx: HandlerContext): Promise<void>;
3
+ export declare function handleResultAddBulk(ctx: HandlerContext): Promise<void>;
4
+ //# sourceMappingURL=result-write.d.ts.map
@@ -0,0 +1,40 @@
1
+ import { parseId } from '../ids.js';
2
+ import { resolveBody } from '../body.js';
3
+ import { AddResultPayloadSchema, AddResultsForCasesPayloadSchema } from '../../schemas.js';
4
+ export async function handleResultAdd(ctx) {
5
+ const runId = parseId(ctx.args.pathParams[0], 'run_id');
6
+ const caseId = parseId(ctx.args.pathParams[1], 'case_id');
7
+ const body = resolveBody(ctx.bodyInput, AddResultPayloadSchema);
8
+ if (!body.ok)
9
+ throw new Error(body.error);
10
+ if (ctx.dryRun) {
11
+ ctx.out({
12
+ dryRun: true,
13
+ action: 'result add',
14
+ runId,
15
+ caseId,
16
+ payload: body.payload,
17
+ source: body.source,
18
+ });
19
+ return;
20
+ }
21
+ ctx.out(await ctx.client.addResultForCase(runId, caseId, body.payload));
22
+ }
23
+ export async function handleResultAddBulk(ctx) {
24
+ const runId = parseId(ctx.args.pathParams[0], 'run_id');
25
+ const body = resolveBody(ctx.bodyInput, AddResultsForCasesPayloadSchema);
26
+ if (!body.ok)
27
+ throw new Error(body.error);
28
+ if (ctx.dryRun) {
29
+ ctx.out({
30
+ dryRun: true,
31
+ action: 'result add-bulk',
32
+ runId,
33
+ payload: body.payload,
34
+ source: body.source,
35
+ });
36
+ return;
37
+ }
38
+ ctx.out(await ctx.client.addResultsForCases(runId, body.payload));
39
+ }
40
+ //# sourceMappingURL=result-write.js.map
@@ -0,0 +1,3 @@
1
+ import type { HandlerContext } from '../handler-context.js';
2
+ export declare function handleResultList(ctx: HandlerContext): Promise<void>;
3
+ //# sourceMappingURL=result.d.ts.map
@@ -0,0 +1,11 @@
1
+ import { parseId, optInt } from '../ids.js';
2
+ export async function handleResultList(ctx) {
3
+ const rid = parseId(ctx.args.runId, '--run-id');
4
+ const limit = optInt(ctx.args.limit);
5
+ const offset = optInt(ctx.args.offset);
6
+ ctx.out(await ctx.client.getResultsForRun(rid, {
7
+ ...(limit !== undefined && { limit }),
8
+ ...(offset !== undefined && { offset }),
9
+ }));
10
+ }
11
+ //# sourceMappingURL=result.js.map
@@ -0,0 +1,10 @@
1
+ import type { HandlerContext } from '../handler-context.js';
2
+ export declare function handleRunAdd(ctx: HandlerContext): Promise<void>;
3
+ /**
4
+ * Close a run. Unlike the other write actions this takes no body — just a
5
+ * single `run_id` path param. POST has no payload, so the body-source
6
+ * resolver is not consulted; any `--data` / `--data-file` / stdin supplied
7
+ * for this action is silently ignored.
8
+ */
9
+ export declare function handleRunClose(ctx: HandlerContext): Promise<void>;
10
+ //# sourceMappingURL=run-write.d.ts.map
@@ -0,0 +1,29 @@
1
+ import { parseId } from '../ids.js';
2
+ import { resolveBody } from '../body.js';
3
+ import { AddRunPayloadSchema } from '../../schemas.js';
4
+ export async function handleRunAdd(ctx) {
5
+ const projectId = parseId(ctx.args.pathParams[0], 'project_id');
6
+ const body = resolveBody(ctx.bodyInput, AddRunPayloadSchema);
7
+ if (!body.ok)
8
+ throw new Error(body.error);
9
+ if (ctx.dryRun) {
10
+ ctx.out({ dryRun: true, action: 'run add', projectId, payload: body.payload, source: body.source });
11
+ return;
12
+ }
13
+ ctx.out(await ctx.client.addRun(projectId, body.payload));
14
+ }
15
+ /**
16
+ * Close a run. Unlike the other write actions this takes no body — just a
17
+ * single `run_id` path param. POST has no payload, so the body-source
18
+ * resolver is not consulted; any `--data` / `--data-file` / stdin supplied
19
+ * for this action is silently ignored.
20
+ */
21
+ export async function handleRunClose(ctx) {
22
+ const runId = parseId(ctx.args.pathParams[0], 'run_id');
23
+ if (ctx.dryRun) {
24
+ ctx.out({ dryRun: true, action: 'run close', runId });
25
+ return;
26
+ }
27
+ ctx.out(await ctx.client.closeRun(runId));
28
+ }
29
+ //# sourceMappingURL=run-write.js.map
@@ -0,0 +1,4 @@
1
+ import type { HandlerContext } from '../handler-context.js';
2
+ export declare function handleRunGet(ctx: HandlerContext): Promise<void>;
3
+ export declare function handleRunList(ctx: HandlerContext): Promise<void>;
4
+ //# sourceMappingURL=run.d.ts.map
@@ -0,0 +1,15 @@
1
+ import { parseId, optInt } from '../ids.js';
2
+ export async function handleRunGet(ctx) {
3
+ const id = parseId(ctx.args.pathParams[0], 'run id');
4
+ ctx.out(await ctx.client.getRun(id));
5
+ }
6
+ export async function handleRunList(ctx) {
7
+ const pid = parseId(ctx.args.projectId, '--project-id');
8
+ const limit = optInt(ctx.args.limit);
9
+ const offset = optInt(ctx.args.offset);
10
+ ctx.out(await ctx.client.getRuns(pid, {
11
+ ...(limit !== undefined && { limit }),
12
+ ...(offset !== undefined && { offset }),
13
+ }));
14
+ }
15
+ //# sourceMappingURL=run.js.map
@@ -0,0 +1,4 @@
1
+ import type { HandlerContext } from '../handler-context.js';
2
+ export declare function handleSuiteGet(ctx: HandlerContext): Promise<void>;
3
+ export declare function handleSuiteList(ctx: HandlerContext): Promise<void>;
4
+ //# sourceMappingURL=suite.d.ts.map
@@ -0,0 +1,10 @@
1
+ import { parseId } from '../ids.js';
2
+ export async function handleSuiteGet(ctx) {
3
+ const id = parseId(ctx.args.pathParams[0], 'suite id');
4
+ ctx.out(await ctx.client.getSuite(id));
5
+ }
6
+ export async function handleSuiteList(ctx) {
7
+ const pid = parseId(ctx.args.projectId, '--project-id');
8
+ ctx.out(await ctx.client.getSuites(pid));
9
+ }
10
+ //# sourceMappingURL=suite.js.map
@@ -0,0 +1,4 @@
1
+ import type { HandlerContext } from '../handler-context.js';
2
+ export declare function handleUserGet(ctx: HandlerContext): Promise<void>;
3
+ export declare function handleUserList(ctx: HandlerContext): Promise<void>;
4
+ //# sourceMappingURL=user.d.ts.map
@@ -0,0 +1,11 @@
1
+ import { parseId, optInt } from '../ids.js';
2
+ export async function handleUserGet(ctx) {
3
+ const id = parseId(ctx.args.pathParams[0], 'user id');
4
+ ctx.out(await ctx.client.getUser(id));
5
+ }
6
+ export async function handleUserList(ctx) {
7
+ const limit = optInt(ctx.args.limit);
8
+ const offset = optInt(ctx.args.offset);
9
+ ctx.out(await ctx.client.getUsers(limit, offset));
10
+ }
11
+ //# sourceMappingURL=user.js.map
@@ -0,0 +1,6 @@
1
+ export declare class IdParseError extends Error {
2
+ constructor(message: string);
3
+ }
4
+ export declare function parseId(raw: string | undefined, name: string): number;
5
+ export declare function optInt(raw: string | undefined): number | undefined;
6
+ //# sourceMappingURL=ids.d.ts.map
@@ -0,0 +1,20 @@
1
+ export class IdParseError extends Error {
2
+ constructor(message) {
3
+ super(message);
4
+ this.name = 'IdParseError';
5
+ }
6
+ }
7
+ export function parseId(raw, name) {
8
+ const n = Number(raw);
9
+ if (raw === undefined || raw === '' || !Number.isInteger(n) || n <= 0) {
10
+ throw new IdParseError(`${name} must be a positive integer (got: ${raw ?? '(none)'})`);
11
+ }
12
+ return n;
13
+ }
14
+ export function optInt(raw) {
15
+ if (raw === undefined)
16
+ return undefined;
17
+ const n = Number(raw);
18
+ return Number.isInteger(n) && n >= 0 ? n : undefined;
19
+ }
20
+ //# sourceMappingURL=ids.js.map
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map