rigorix-client 0.0.0-stage → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,3 +1,59 @@
1
- # Temporary Holding Version
1
+ # rigorix-client (TypeScript)
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Protocol-only typed client for the frozen `rigorix.*` JSON-RPC catalog
4
+ ([ADR-0001](../../docs/adr/ADR-0001-native-api-catalog.md)). It mirrors the
5
+ catalog `$defs` and speaks JSON-RPC 2.0 over `POST /rpc` (single requests,
6
+ batch arrays, notifications).
7
+
8
+ Two invariants are enforced in code and CI:
9
+
10
+ - **Governed flows only** — no raw step-execution surface (ADR-0001 D10); a
11
+ test fails if one is ever added.
12
+ - **No engine dependency** — execution logic never moves into an SDK (D-1).
13
+
14
+ ## Install
15
+
16
+ ```bash
17
+ npm install rigorix-client@0.2.0
18
+ ```
19
+
20
+ ## Usage
21
+
22
+ ```ts
23
+ import { Client, FetchTransport } from "rigorix-client";
24
+
25
+ const client = new Client(new FetchTransport("https://host/rpc", { authorization: "Bearer …" }));
26
+
27
+ const plan = await client.plan({ template_name: "attendance" });
28
+ const run = await client.run({ template_name: "attendance" });
29
+ const version = await client.version(); // checks the apiVersion major constraint
30
+
31
+ await client.notify("rigorix.audit.read", { execution_id: run.execution_id });
32
+ const batch = await client.batchRaw([new BatchCall("rigorix.audit.list", {})]);
33
+ ```
34
+
35
+ ## Transport seam
36
+
37
+ Implement `Transport` (`post(body) => response`), or use the built-in
38
+ `FetchTransport` (global `fetch`, Node 20+) / `MockTransport` (tests).
39
+
40
+ ## Errors
41
+
42
+ `errors.json` `data.type` maps to a distinct `ApiError.kind`; an unknown type
43
+ falls through to `"unknown"` — never a throw. `JsonRpcError` wraps a JSON-RPC
44
+ `error`; `TransportError`/`ProtocolError`/`VersionMismatchError` cover the rest.
45
+
46
+ ## Catalog binding + drift
47
+
48
+ `catalog.METHODS` lists every frozen method as `typed` or `server_only`.
49
+ `catalog_drift` reads `schemas/api/catalog.json` and fails if the two diverge —
50
+ the namespace is closed, so no method is silently omitted. Server-only
51
+ exemptions: `audit.head`/`.history`/`.overdue` (enterprise Phase B),
52
+ `auth.verify` (enterprise JWKS), `policy.bundle` (admin control plane).
53
+
54
+ ## Test
55
+
56
+ ```bash
57
+ npm install
58
+ npm test # tsc + node --test
59
+ ```
@@ -0,0 +1,9 @@
1
+ /** Device-flow auth helpers (ADR-0008): conveniences over `rigorix.auth.*`.
2
+ * The client never invents identity — the attested session is authoritative
3
+ * (ADR-0001 D5); `approver_id`/`authority` are display-only. */
4
+ import type { Client } from "./client.js";
5
+ import type { AuthLoginParams, AuthLoginResult, AuthStatusResult } from "./types.js";
6
+ /** Begin the OIDC device flow (`rigorix.auth.login`). */
7
+ export declare function beginDeviceFlow(client: Client, params: AuthLoginParams): Promise<AuthLoginResult>;
8
+ /** Report the redacted session status (`rigorix.auth.status`). */
9
+ export declare function sessionStatus(client: Client): Promise<AuthStatusResult>;
@@ -0,0 +1,11 @@
1
+ /** Device-flow auth helpers (ADR-0008): conveniences over `rigorix.auth.*`.
2
+ * The client never invents identity — the attested session is authoritative
3
+ * (ADR-0001 D5); `approver_id`/`authority` are display-only. */
4
+ /** Begin the OIDC device flow (`rigorix.auth.login`). */
5
+ export function beginDeviceFlow(client, params) {
6
+ return client.authLogin(params);
7
+ }
8
+ /** Report the redacted session status (`rigorix.auth.status`). */
9
+ export function sessionStatus(client) {
10
+ return client.authStatus();
11
+ }
@@ -0,0 +1,21 @@
1
+ /** The frozen `rigorix.*` catalog as a client-side binding table (ADR-0001 D3).
2
+ *
3
+ * Every method in `schemas/api/catalog.json` is either **typed** or
4
+ * **explicitly server-only**. The `catalog_drift` test fails if the two
5
+ * diverge — the namespace is closed, so no method is silently omitted. There is
6
+ * deliberately no raw step-execution method (ADR-0001 D10). */
7
+ export type Binding = {
8
+ kind: "typed";
9
+ } | {
10
+ kind: "server_only";
11
+ reason: string;
12
+ };
13
+ export interface MethodBinding {
14
+ name: string;
15
+ binding: Binding;
16
+ }
17
+ export declare const METHODS: MethodBinding[];
18
+ export declare function find(name: string): MethodBinding | undefined;
19
+ export declare function names(): string[];
20
+ /** Ensure the client never exposes a raw step-execution method (ADR-0001 D10). */
21
+ export declare function assertNoRawStepExecution(): void;
@@ -0,0 +1,64 @@
1
+ /** The frozen `rigorix.*` catalog as a client-side binding table (ADR-0001 D3).
2
+ *
3
+ * Every method in `schemas/api/catalog.json` is either **typed** or
4
+ * **explicitly server-only**. The `catalog_drift` test fails if the two
5
+ * diverge — the namespace is closed, so no method is silently omitted. There is
6
+ * deliberately no raw step-execution method (ADR-0001 D10). */
7
+ export const METHODS = [
8
+ { name: "rigorix.plan", binding: { kind: "typed" } },
9
+ { name: "rigorix.validatePlan", binding: { kind: "typed" } },
10
+ { name: "rigorix.run", binding: { kind: "typed" } },
11
+ { name: "rigorix.execute", binding: { kind: "typed" } },
12
+ { name: "rigorix.approve", binding: { kind: "typed" } },
13
+ { name: "rigorix.checkEnforcement", binding: { kind: "typed" } },
14
+ { name: "rigorix.audit.read", binding: { kind: "typed" } },
15
+ { name: "rigorix.audit.list", binding: { kind: "typed" } },
16
+ { name: "rigorix.audit.summary", binding: { kind: "typed" } },
17
+ {
18
+ name: "rigorix.audit.head",
19
+ binding: { kind: "server_only", reason: "enterprise ADR-016 Phase B ledger surface" },
20
+ },
21
+ {
22
+ name: "rigorix.audit.history",
23
+ binding: { kind: "server_only", reason: "enterprise ADR-016 Phase B ledger surface" },
24
+ },
25
+ {
26
+ name: "rigorix.audit.overdue",
27
+ binding: { kind: "server_only", reason: "enterprise ADR-016 Phase B ledger surface" },
28
+ },
29
+ { name: "rigorix.template.list", binding: { kind: "typed" } },
30
+ { name: "rigorix.template.get", binding: { kind: "typed" } },
31
+ { name: "rigorix.template.create", binding: { kind: "typed" } },
32
+ { name: "rigorix.template.validate", binding: { kind: "typed" } },
33
+ { name: "rigorix.auth.login", binding: { kind: "typed" } },
34
+ { name: "rigorix.auth.status", binding: { kind: "typed" } },
35
+ { name: "rigorix.auth.logout", binding: { kind: "typed" } },
36
+ {
37
+ name: "rigorix.auth.verify",
38
+ binding: { kind: "server_only", reason: "enterprise-side JWKS claim verification" },
39
+ },
40
+ {
41
+ name: "rigorix.policy.bundle",
42
+ binding: { kind: "server_only", reason: "admin/enterprise control-plane export" },
43
+ },
44
+ { name: "rigorix.usageGuide", binding: { kind: "typed" } },
45
+ { name: "rigorix.system.version", binding: { kind: "typed" } },
46
+ ];
47
+ export function find(name) {
48
+ return METHODS.find((method) => method.name === name);
49
+ }
50
+ export function names() {
51
+ return METHODS.map((method) => method.name);
52
+ }
53
+ /** Ensure the client never exposes a raw step-execution method (ADR-0001 D10). */
54
+ export function assertNoRawStepExecution() {
55
+ for (const method of METHODS) {
56
+ const lower = method.name.toLowerCase();
57
+ if (lower.includes("step") && method.name !== "rigorix.plan") {
58
+ throw new Error(`unexpected raw step-execution binding: ${method.name}`);
59
+ }
60
+ if (method.name === "rigorix.executeStep") {
61
+ throw new Error("raw step execution must never be bound");
62
+ }
63
+ }
64
+ }
@@ -0,0 +1,51 @@
1
+ /** The typed client over a `Transport` (ADR-0001 D1/D3). */
2
+ import { ClientError } from "./error.js";
3
+ import { type RpcResponse } from "./jsonrpc.js";
4
+ import type { Transport } from "./transport.js";
5
+ import type * as types from "./types.js";
6
+ /** The native API major this client targets (catalog.json `apiVersion`). */
7
+ export declare const CLIENT_API_MAJOR = 1;
8
+ /** The full native API version this client was built against. */
9
+ export declare const CLIENT_API_VERSION = "1.0.0";
10
+ /** One raw call in a batch. */
11
+ export declare class BatchCall {
12
+ readonly method: string;
13
+ readonly params?: unknown | undefined;
14
+ constructor(method: string, params?: unknown | undefined);
15
+ }
16
+ /** A typed JSON-RPC client over the frozen `rigorix.*` catalog. */
17
+ export declare class Client {
18
+ private readonly transport;
19
+ private nextId;
20
+ constructor(transport: Transport);
21
+ private id;
22
+ /** Call a method and return the raw `result` value. */
23
+ callRaw(method: string, params?: unknown): Promise<unknown>;
24
+ /** Call a method with typed params, returning a typed result. */
25
+ call<P, R>(method: string, params: P): Promise<R>;
26
+ /** Send a notification (no `id`; no response expected). */
27
+ notify(method: string, params?: unknown): Promise<void>;
28
+ /** Send a batch (one JSON array) and return the raw responses. */
29
+ batchRaw(calls: BatchCall[]): Promise<RpcResponse[]>;
30
+ plan(params: types.PlanParams): Promise<types.PlanResult>;
31
+ validatePlan(params: types.ValidatePlanParams): Promise<types.ValidateResult>;
32
+ run(params: types.RunParams): Promise<types.ExecuteResult>;
33
+ execute(params: types.ExecuteParams): Promise<types.ExecuteResult>;
34
+ approve(params: types.ApproveParams): Promise<types.ApproveResult>;
35
+ checkEnforcement(): Promise<types.CheckEnforcementResult>;
36
+ auditRead(params: types.AuditReadParams): Promise<types.AuditReadResult>;
37
+ auditList(params: types.AuditListParams): Promise<types.AuditListResult>;
38
+ auditSummary(params: types.AuditSummaryParams): Promise<types.AuditSummaryResult>;
39
+ templateList(params: types.TemplateListParams): Promise<types.TemplateListResult>;
40
+ templateGet(params: types.TemplateGetParams): Promise<types.TemplateGetResult>;
41
+ templateCreate(params: types.TemplateCreateParams): Promise<types.TemplateCreateResult>;
42
+ templateValidate(params: types.TemplateValidateParams): Promise<types.ValidateResult>;
43
+ authLogin(params: types.AuthLoginParams): Promise<types.AuthLoginResult>;
44
+ authStatus(): Promise<types.AuthStatusResult>;
45
+ authLogout(): Promise<types.AuthLogoutResult>;
46
+ usageGuide(): Promise<types.UsageGuideResult>;
47
+ version(): Promise<types.VersionResult>;
48
+ /** Verify the server's `api_version` is compatible with this client's major. */
49
+ checkVersion(version: types.VersionResult): void;
50
+ }
51
+ export { ClientError };
@@ -0,0 +1,122 @@
1
+ /** The typed client over a `Transport` (ADR-0001 D1/D3). */
2
+ import { ClientError, JsonRpcError, ProtocolError, VersionMismatchError, } from "./error.js";
3
+ import { call, decode, decodeBatch, encode, encodeBatch, notification, } from "./jsonrpc.js";
4
+ /** The native API major this client targets (catalog.json `apiVersion`). */
5
+ export const CLIENT_API_MAJOR = 1;
6
+ /** The full native API version this client was built against. */
7
+ export const CLIENT_API_VERSION = "1.0.0";
8
+ /** One raw call in a batch. */
9
+ export class BatchCall {
10
+ method;
11
+ params;
12
+ constructor(method, params) {
13
+ this.method = method;
14
+ this.params = params;
15
+ }
16
+ }
17
+ /** A typed JSON-RPC client over the frozen `rigorix.*` catalog. */
18
+ export class Client {
19
+ transport;
20
+ nextId = 1;
21
+ constructor(transport) {
22
+ this.transport = transport;
23
+ }
24
+ id() {
25
+ return this.nextId++;
26
+ }
27
+ /** Call a method and return the raw `result` value. */
28
+ async callRaw(method, params) {
29
+ const body = encode(call(this.id(), method, params));
30
+ const responseBody = await this.transport.post(body);
31
+ const response = decode(responseBody);
32
+ if (response.error)
33
+ throw new JsonRpcError(response.error);
34
+ if (response.result === undefined) {
35
+ throw new ProtocolError("response has neither `result` nor `error`");
36
+ }
37
+ return response.result;
38
+ }
39
+ /** Call a method with typed params, returning a typed result. */
40
+ async call(method, params) {
41
+ return (await this.callRaw(method, params));
42
+ }
43
+ /** Send a notification (no `id`; no response expected). */
44
+ async notify(method, params) {
45
+ await this.transport.post(encode(notification(method, params)));
46
+ }
47
+ /** Send a batch (one JSON array) and return the raw responses. */
48
+ async batchRaw(calls) {
49
+ const body = encodeBatch(calls.map((entry) => call(this.id(), entry.method, entry.params)));
50
+ const responseBody = await this.transport.post(body);
51
+ return decodeBatch(responseBody);
52
+ }
53
+ // ─── Governed flows ──────────────────────────────────────────────────
54
+ plan(params) {
55
+ return this.call("rigorix.plan", params);
56
+ }
57
+ validatePlan(params) {
58
+ return this.call("rigorix.validatePlan", params);
59
+ }
60
+ run(params) {
61
+ return this.call("rigorix.run", params);
62
+ }
63
+ execute(params) {
64
+ return this.call("rigorix.execute", params);
65
+ }
66
+ approve(params) {
67
+ return this.call("rigorix.approve", params);
68
+ }
69
+ checkEnforcement() {
70
+ return this.call("rigorix.checkEnforcement", {});
71
+ }
72
+ // ─── Audit (read side) ───────────────────────────────────────────────
73
+ auditRead(params) {
74
+ return this.call("rigorix.audit.read", params);
75
+ }
76
+ auditList(params) {
77
+ return this.call("rigorix.audit.list", params);
78
+ }
79
+ auditSummary(params) {
80
+ return this.call("rigorix.audit.summary", params);
81
+ }
82
+ // ─── Templates ───────────────────────────────────────────────────────
83
+ templateList(params) {
84
+ return this.call("rigorix.template.list", params);
85
+ }
86
+ templateGet(params) {
87
+ return this.call("rigorix.template.get", params);
88
+ }
89
+ templateCreate(params) {
90
+ return this.call("rigorix.template.create", params);
91
+ }
92
+ templateValidate(params) {
93
+ return this.call("rigorix.template.validate", params);
94
+ }
95
+ // ─── Auth (device flow) ──────────────────────────────────────────────
96
+ authLogin(params) {
97
+ return this.call("rigorix.auth.login", params);
98
+ }
99
+ authStatus() {
100
+ return this.call("rigorix.auth.status", {});
101
+ }
102
+ authLogout() {
103
+ return this.call("rigorix.auth.logout", {});
104
+ }
105
+ // ─── Self-description / negotiation ──────────────────────────────────
106
+ usageGuide() {
107
+ return this.call("rigorix.usageGuide", {});
108
+ }
109
+ async version() {
110
+ const version = await this.call("rigorix.system.version", {});
111
+ this.checkVersion(version);
112
+ return version;
113
+ }
114
+ /** Verify the server's `api_version` is compatible with this client's major. */
115
+ checkVersion(version) {
116
+ const major = Number.parseInt(version.api_version.split(".")[0] ?? "", 10);
117
+ if (major !== CLIENT_API_MAJOR) {
118
+ throw new VersionMismatchError(version.api_version, CLIENT_API_MAJOR);
119
+ }
120
+ }
121
+ }
122
+ export { ClientError };
@@ -0,0 +1,49 @@
1
+ /** Error taxonomy mapping (`schemas/api/errors.json`, ADR-0001 D6).
2
+ *
3
+ * `data.type` is the stable machine identifier. Every declared type maps to a
4
+ * distinct `ApiError.kind`; an unknown type falls through to `"unknown"` —
5
+ * never a throw. */
6
+ export declare const API_ERROR_TYPES: readonly ["parse_error", "invalid_request", "method_not_found", "invalid_params", "internal_error", "not_authenticated", "insufficient_scope", "denied_by_sequence", "policy_violation", "approval_required", "identity_required", "budget_exhausted", "timeout", "api_error", "not_enabled"];
7
+ export type ApiErrorType = (typeof API_ERROR_TYPES)[number];
8
+ /** The stable `data.type` value for a typed error, or `"unknown"`. */
9
+ export declare class ApiError extends Error {
10
+ readonly kind: ApiErrorType | "unknown";
11
+ readonly type: string;
12
+ readonly code?: number;
13
+ readonly data?: unknown;
14
+ constructor(type: string, message: string, code?: number, data?: unknown);
15
+ /** `data.rule_id`, when present (`denied_by_sequence`). */
16
+ get ruleId(): string | undefined;
17
+ /** `data.step`, when present (`denied_by_sequence`). */
18
+ get step(): string | undefined;
19
+ }
20
+ /** The JSON-RPC `error` object shape. */
21
+ export interface RpcErrorShape {
22
+ code: number;
23
+ message: string;
24
+ data?: {
25
+ type?: string;
26
+ [key: string]: unknown;
27
+ };
28
+ }
29
+ /** Base class for every client-side failure. */
30
+ export declare class ClientError extends Error {
31
+ }
32
+ /** The transport failed (network/IO or non-2xx). */
33
+ export declare class TransportError extends ClientError {
34
+ }
35
+ /** A malformed JSON-RPC response (neither `result` nor `error`). */
36
+ export declare class ProtocolError extends ClientError {
37
+ }
38
+ /** A JSON-RPC `error` object, mapped to the typed taxonomy. */
39
+ export declare class JsonRpcError extends ClientError {
40
+ readonly rpc: RpcErrorShape;
41
+ constructor(rpc: RpcErrorShape);
42
+ get apiError(): ApiError;
43
+ }
44
+ /** The server's `api_version` is incompatible with this client's major. */
45
+ export declare class VersionMismatchError extends ClientError {
46
+ readonly server: string;
47
+ readonly clientMajor: number;
48
+ constructor(server: string, clientMajor: number);
49
+ }
@@ -0,0 +1,83 @@
1
+ /** Error taxonomy mapping (`schemas/api/errors.json`, ADR-0001 D6).
2
+ *
3
+ * `data.type` is the stable machine identifier. Every declared type maps to a
4
+ * distinct `ApiError.kind`; an unknown type falls through to `"unknown"` —
5
+ * never a throw. */
6
+ export const API_ERROR_TYPES = [
7
+ "parse_error",
8
+ "invalid_request",
9
+ "method_not_found",
10
+ "invalid_params",
11
+ "internal_error",
12
+ "not_authenticated",
13
+ "insufficient_scope",
14
+ "denied_by_sequence",
15
+ "policy_violation",
16
+ "approval_required",
17
+ "identity_required",
18
+ "budget_exhausted",
19
+ "timeout",
20
+ "api_error",
21
+ "not_enabled",
22
+ ];
23
+ /** The stable `data.type` value for a typed error, or `"unknown"`. */
24
+ export class ApiError extends Error {
25
+ kind;
26
+ type;
27
+ code;
28
+ data;
29
+ constructor(type, message, code, data) {
30
+ super(`${type}: ${message}`);
31
+ this.name = "ApiError";
32
+ this.type = type;
33
+ this.kind = API_ERROR_TYPES.includes(type)
34
+ ? type
35
+ : "unknown";
36
+ this.code = code;
37
+ this.data = data;
38
+ }
39
+ /** `data.rule_id`, when present (`denied_by_sequence`). */
40
+ get ruleId() {
41
+ const value = this.data?.rule_id;
42
+ return typeof value === "string" ? value : undefined;
43
+ }
44
+ /** `data.step`, when present (`denied_by_sequence`). */
45
+ get step() {
46
+ const value = this.data?.step;
47
+ return typeof value === "string" ? value : undefined;
48
+ }
49
+ }
50
+ /** Base class for every client-side failure. */
51
+ export class ClientError extends Error {
52
+ }
53
+ /** The transport failed (network/IO or non-2xx). */
54
+ export class TransportError extends ClientError {
55
+ }
56
+ /** A malformed JSON-RPC response (neither `result` nor `error`). */
57
+ export class ProtocolError extends ClientError {
58
+ }
59
+ /** A JSON-RPC `error` object, mapped to the typed taxonomy. */
60
+ export class JsonRpcError extends ClientError {
61
+ rpc;
62
+ constructor(rpc) {
63
+ super(`json-rpc error ${rpc.code}: ${rpc.message}`);
64
+ this.rpc = rpc;
65
+ this.name = "JsonRpcError";
66
+ }
67
+ get apiError() {
68
+ const data = this.rpc.data;
69
+ const type = typeof data?.type === "string" ? data.type : "unknown";
70
+ return new ApiError(type, this.rpc.message, this.rpc.code, data);
71
+ }
72
+ }
73
+ /** The server's `api_version` is incompatible with this client's major. */
74
+ export class VersionMismatchError extends ClientError {
75
+ server;
76
+ clientMajor;
77
+ constructor(server, clientMajor) {
78
+ super(`server apiVersion ${server} is incompatible with the client constraint (major ${clientMajor})`);
79
+ this.server = server;
80
+ this.clientMajor = clientMajor;
81
+ this.name = "VersionMismatchError";
82
+ }
83
+ }
@@ -0,0 +1,10 @@
1
+ /** rigorix-client (TypeScript) — protocol-only typed client for the frozen
2
+ * `rigorix.*` JSON-RPC catalog (ADR-0001). Governed flows only; no raw step
3
+ * execution; no engine dependency. */
4
+ export * from "./auth.js";
5
+ export * from "./catalog.js";
6
+ export * from "./client.js";
7
+ export * from "./error.js";
8
+ export * from "./jsonrpc.js";
9
+ export * from "./transport.js";
10
+ export type * from "./types.js";
@@ -0,0 +1,9 @@
1
+ /** rigorix-client (TypeScript) — protocol-only typed client for the frozen
2
+ * `rigorix.*` JSON-RPC catalog (ADR-0001). Governed flows only; no raw step
3
+ * execution; no engine dependency. */
4
+ export * from "./auth.js";
5
+ export * from "./catalog.js";
6
+ export * from "./client.js";
7
+ export * from "./error.js";
8
+ export * from "./jsonrpc.js";
9
+ export * from "./transport.js";
@@ -0,0 +1,23 @@
1
+ /** JSON-RPC 2.0 framing (ADR-0001 D1): single requests, batch arrays, and
2
+ * notifications (requests with no `id`). */
3
+ import type { RpcErrorShape } from "./error.js";
4
+ export interface RpcRequest {
5
+ jsonrpc: "2.0";
6
+ method: string;
7
+ params?: unknown;
8
+ id?: number | string;
9
+ }
10
+ export interface RpcResponse {
11
+ jsonrpc?: string;
12
+ id?: number | string;
13
+ result?: unknown;
14
+ error?: RpcErrorShape;
15
+ }
16
+ /** A request expecting a response. */
17
+ export declare function call(id: number | string, method: string, params?: unknown): RpcRequest;
18
+ /** A notification — no `id`, no response expected. */
19
+ export declare function notification(method: string, params?: unknown): RpcRequest;
20
+ export declare function encode(request: RpcRequest): string;
21
+ export declare function encodeBatch(requests: RpcRequest[]): string;
22
+ export declare function decode<T = RpcResponse>(body: string): T;
23
+ export declare function decodeBatch(body: string): RpcResponse[];
@@ -0,0 +1,22 @@
1
+ /** JSON-RPC 2.0 framing (ADR-0001 D1): single requests, batch arrays, and
2
+ * notifications (requests with no `id`). */
3
+ /** A request expecting a response. */
4
+ export function call(id, method, params) {
5
+ return { jsonrpc: "2.0", method, params, id };
6
+ }
7
+ /** A notification — no `id`, no response expected. */
8
+ export function notification(method, params) {
9
+ return { jsonrpc: "2.0", method, params };
10
+ }
11
+ export function encode(request) {
12
+ return JSON.stringify(request);
13
+ }
14
+ export function encodeBatch(requests) {
15
+ return JSON.stringify(requests);
16
+ }
17
+ export function decode(body) {
18
+ return JSON.parse(body);
19
+ }
20
+ export function decodeBatch(body) {
21
+ return JSON.parse(body);
22
+ }
@@ -0,0 +1,19 @@
1
+ /** Transport seam — the crate depends on no HTTP framework. */
2
+ export interface Transport {
3
+ /** POST a JSON-RPC body and return the raw response body. */
4
+ post(body: string): Promise<string>;
5
+ }
6
+ /** A `fetch`-based transport (Node 20+ has a global `fetch`). */
7
+ export declare class FetchTransport implements Transport {
8
+ private readonly url;
9
+ private readonly headers;
10
+ constructor(url: string, headers?: Record<string, string>);
11
+ post(body: string): Promise<string>;
12
+ }
13
+ /** A test transport that records requests and replays queued responses. */
14
+ export declare class MockTransport implements Transport {
15
+ private readonly responses;
16
+ readonly requests: string[];
17
+ constructor(responses?: string[]);
18
+ post(body: string): Promise<string>;
19
+ }
@@ -0,0 +1,38 @@
1
+ /** Transport seam — the crate depends on no HTTP framework. */
2
+ import { TransportError } from "./error.js";
3
+ /** A `fetch`-based transport (Node 20+ has a global `fetch`). */
4
+ export class FetchTransport {
5
+ url;
6
+ headers;
7
+ constructor(url, headers = {}) {
8
+ this.url = url;
9
+ this.headers = headers;
10
+ }
11
+ async post(body) {
12
+ const response = await fetch(this.url, {
13
+ method: "POST",
14
+ headers: { "content-type": "application/json", ...this.headers },
15
+ body,
16
+ });
17
+ if (!response.ok) {
18
+ throw new TransportError(`transport: HTTP ${response.status}`);
19
+ }
20
+ return response.text();
21
+ }
22
+ }
23
+ /** A test transport that records requests and replays queued responses. */
24
+ export class MockTransport {
25
+ responses;
26
+ requests = [];
27
+ constructor(responses = []) {
28
+ this.responses = responses;
29
+ }
30
+ async post(body) {
31
+ this.requests.push(body);
32
+ const response = this.responses.shift();
33
+ if (response === undefined) {
34
+ throw new TransportError("mock transport: no queued response");
35
+ }
36
+ return response;
37
+ }
38
+ }
@@ -0,0 +1,237 @@
1
+ /** Typed request/result interfaces mirroring `schemas/api/catalog.json` `$defs`
2
+ * (ADR-0001). Field names match the frozen catalog exactly (snake_case). */
3
+ export interface EmptyParams {
4
+ }
5
+ export interface PlanParams {
6
+ template_name: string;
7
+ }
8
+ export interface PlanResult {
9
+ template_name: string;
10
+ description: string;
11
+ version: string;
12
+ tags: string[];
13
+ graph: unknown;
14
+ constraints?: Constraints | null;
15
+ enforcement: unknown;
16
+ }
17
+ export interface Constraints {
18
+ max_tool_calls?: number | null;
19
+ max_tokens?: number | null;
20
+ max_duration_secs?: number | null;
21
+ blocked_tools?: string[];
22
+ extensions?: Record<string, string>;
23
+ }
24
+ export interface ValidatePlanParams {
25
+ plan: unknown;
26
+ identity?: unknown;
27
+ }
28
+ export interface ValidateResult {
29
+ valid: boolean;
30
+ warnings: string[];
31
+ errors: string[];
32
+ estimated_cost?: CostEstimate | null;
33
+ }
34
+ export interface CostEstimate {
35
+ estimated_tokens: number;
36
+ estimated_tool_calls: number;
37
+ estimated_cost_micro?: number | null;
38
+ }
39
+ export interface RunParams {
40
+ template_name: string;
41
+ execution_id?: string;
42
+ repository?: string;
43
+ author?: string;
44
+ }
45
+ export interface ExecuteParams {
46
+ plan?: unknown;
47
+ template_name?: string;
48
+ execution_id?: string;
49
+ repository?: string;
50
+ author?: string;
51
+ }
52
+ export interface ExecuteResult {
53
+ execution_id: string;
54
+ status: string;
55
+ steps: StepResult[];
56
+ duration_ms: number;
57
+ tokens_used?: number | null;
58
+ audit_uri: string;
59
+ }
60
+ export interface StepResult {
61
+ step_name: string;
62
+ success: boolean;
63
+ error?: string | null;
64
+ output?: unknown;
65
+ duration_ms: number;
66
+ }
67
+ export interface ApproveParams {
68
+ execution_id: string;
69
+ step_names: string[];
70
+ approver_id?: string;
71
+ authority?: string;
72
+ token_claims_ref?: string;
73
+ }
74
+ export interface ApproveResult {
75
+ execution_id: string;
76
+ approved_steps: string[];
77
+ not_found: string[];
78
+ still_pending: string[];
79
+ resumed: boolean;
80
+ message: string;
81
+ final_state?: string | null;
82
+ }
83
+ export interface CheckEnforcementResult {
84
+ active: boolean;
85
+ preset: string;
86
+ budget: BudgetStatus;
87
+ circuit_breakers: CircuitBreaker[];
88
+ }
89
+ export interface BudgetStatus {
90
+ tool_calls_total: number;
91
+ tool_calls_remaining: number;
92
+ tokens_total: number;
93
+ tokens_remaining: number;
94
+ }
95
+ export interface CircuitBreaker {
96
+ name: string;
97
+ is_tripped: boolean;
98
+ description: string;
99
+ }
100
+ export interface AuditReadParams {
101
+ execution_id: string;
102
+ format?: string;
103
+ }
104
+ export interface AuditReadResult {
105
+ execution_id: string;
106
+ status: string;
107
+ template_name?: string | null;
108
+ started_at: string;
109
+ completed_at: string;
110
+ duration_ms: number;
111
+ steps: StepResult[];
112
+ tokens_used?: number | null;
113
+ audit_uri: string;
114
+ }
115
+ export interface AuditListParams {
116
+ status?: string;
117
+ since?: string;
118
+ until?: string;
119
+ template?: string;
120
+ limit?: number;
121
+ }
122
+ export interface AuditListResult {
123
+ total_count: number;
124
+ audits: AuditListItem[];
125
+ }
126
+ export interface AuditListItem {
127
+ execution_id: string;
128
+ status: string;
129
+ template_name?: string | null;
130
+ started_at: string;
131
+ duration_ms: number;
132
+ audit_uri: string;
133
+ }
134
+ export interface AuditSummaryParams {
135
+ since?: string;
136
+ until?: string;
137
+ }
138
+ export interface AuditSummaryResult {
139
+ since: string;
140
+ until: string;
141
+ total_executions: number;
142
+ success_count: number;
143
+ failure_count: number;
144
+ success_rate: number;
145
+ total_duration_ms: number;
146
+ total_tokens?: number | null;
147
+ top_failures: TopFailure[];
148
+ top_templates: TopTemplate[];
149
+ }
150
+ export interface TopFailure {
151
+ description: string;
152
+ count: number;
153
+ template_name?: string | null;
154
+ }
155
+ export interface TopTemplate {
156
+ name: string;
157
+ count: number;
158
+ avg_duration_ms: number;
159
+ }
160
+ export interface TemplateListParams {
161
+ limit?: number;
162
+ search?: string;
163
+ tags?: string[];
164
+ }
165
+ export interface TemplateListResult {
166
+ templates: TemplateListItem[];
167
+ }
168
+ export interface TemplateListItem {
169
+ name: string;
170
+ description: string;
171
+ version: string;
172
+ tags: string[];
173
+ step_count: number;
174
+ updated_at: string;
175
+ }
176
+ export interface TemplateGetParams {
177
+ name: string;
178
+ format?: string;
179
+ }
180
+ export interface TemplateGetResult {
181
+ name: string;
182
+ description: string;
183
+ version: string;
184
+ tags: string[];
185
+ steps: unknown[];
186
+ constraints?: Constraints | null;
187
+ metadata?: Record<string, string> | null;
188
+ created_at: string;
189
+ updated_at: string;
190
+ }
191
+ export interface TemplateCreateParams {
192
+ name: string;
193
+ plan: unknown;
194
+ overwrite?: boolean;
195
+ }
196
+ export interface TemplateCreateResult {
197
+ name: string;
198
+ path: string;
199
+ status: string;
200
+ }
201
+ export interface TemplateValidateParams {
202
+ plan: unknown;
203
+ }
204
+ export interface AuthLoginParams {
205
+ client_id?: string;
206
+ issuer?: string;
207
+ }
208
+ export interface AuthLoginResult {
209
+ status: string;
210
+ verification_uri: string;
211
+ user_code: string;
212
+ expires_in: number;
213
+ }
214
+ export interface AuthStatusResult {
215
+ status: string;
216
+ claim_summary?: ClaimSummary | null;
217
+ source: string;
218
+ }
219
+ export interface ClaimSummary {
220
+ subject: string;
221
+ issuer: string;
222
+ authority?: string | null;
223
+ expires_at?: string | null;
224
+ }
225
+ export interface AuthLogoutResult {
226
+ status: string;
227
+ }
228
+ export interface UsageGuideResult {
229
+ guide: string;
230
+ }
231
+ export interface VersionResult {
232
+ name: string;
233
+ engine_version: string;
234
+ api_version: string;
235
+ schemas: string[];
236
+ capabilities: string[];
237
+ }
@@ -0,0 +1,3 @@
1
+ /** Typed request/result interfaces mirroring `schemas/api/catalog.json` `$defs`
2
+ * (ADR-0001). Field names match the frozen catalog exactly (snake_case). */
3
+ export {};
package/package.json CHANGED
@@ -1,6 +1,35 @@
1
1
  {
2
2
  "name": "rigorix-client",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.4.0",
4
+ "description": "Protocol-only typed client for the frozen rigorix.* JSON-RPC catalog — governed flows only, no raw step execution, no engine dependency.",
5
+ "type": "module",
6
+ "main": "dist/src/index.js",
7
+ "types": "dist/src/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/src/index.d.ts",
11
+ "import": "./dist/src/index.js"
12
+ }
13
+ },
14
+ "files": ["dist/src", "README.md"],
15
+ "license": "Apache-2.0",
16
+ "author": "Arman Wolkenstein-Jalili and Rigorix contributors",
17
+ "keywords": ["rigorix", "jsonrpc", "client", "governance", "sdk"],
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "https://github.com/arman-jalili/rigorix-sdk.git",
21
+ "directory": "typescript-client"
22
+ },
23
+ "engines": {
24
+ "node": ">=20"
25
+ },
26
+ "scripts": {
27
+ "build": "tsc -p tsconfig.json",
28
+ "prepack": "npm run build",
29
+ "test": "npm run build && node --test dist/test/*.test.js"
30
+ },
31
+ "devDependencies": {
32
+ "@types/node": "^22.0.0",
33
+ "typescript": "^5.6.0"
34
+ }
35
+ }