@neuraltrust/trustguard-sdk 0.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.
package/README.md ADDED
@@ -0,0 +1,83 @@
1
+ # TrustGuard Node.js SDK
2
+
3
+ Official Node.js / TypeScript client for the TrustGuard runtime guard API (`POST /v1/guard`). Configure a base URL and an API key, send the payload you want evaluated, and act on the verdict: TrustGuard detects, you enforce.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @neuraltrust/trustguard-sdk
9
+ ```
10
+
11
+ Requires Node.js 18+ (uses the built-in `fetch`). Zero runtime dependencies; ships ESM and CJS with full type definitions.
12
+
13
+ ## Usage
14
+
15
+ ```typescript
16
+ import { TrustGuard } from "@neuraltrust/trustguard-sdk";
17
+
18
+ const client = new TrustGuard({
19
+ baseUrl: "https://guard.neuraltrust.ai",
20
+ apiKey: process.env.TRUSTGUARD_API_KEY!,
21
+ });
22
+
23
+ const response = await client.guard({ input: { prompt: "user text to evaluate" } });
24
+ if (response.isFlagged) {
25
+ // block the request
26
+ }
27
+ ```
28
+
29
+ ### Options
30
+
31
+ ```typescript
32
+ const response = await client.guard({
33
+ input: { prompt: "user text" },
34
+ direction: "output", // "input" (default) or "output"
35
+ sessionId: "conversation-42", // groups multi-turn traffic
36
+ consumerId: "user-7", // the end user behind the request
37
+ });
38
+ ```
39
+
40
+ ### Attachments
41
+
42
+ Documents are base64-encoded into `metadata.attachments` for file-aware plugins:
43
+
44
+ ```typescript
45
+ const response = await client.guard({
46
+ input: { prompt: "summarize this file" },
47
+ attachments: [{ filename: "doc.pdf", contentType: "application/pdf", data: pdfBytes }],
48
+ });
49
+ ```
50
+
51
+ ### Response
52
+
53
+ | Field | Meaning |
54
+ |---|---|
55
+ | `isFlagged` | The enforcement signal: block when `true` |
56
+ | `findings` | What every plugin in the policy chain reported (`detectionType`, `confidence`, `ruleName`, `details`) |
57
+ | `transformedPayload` | The payload as rewritten by in-flight masking, `null` when untouched |
58
+ | `traceId` / `requestId` | Correlation ids for TrustGuard telemetry |
59
+
60
+ ### Errors
61
+
62
+ Non-2xx responses throw `TrustGuardAPIError` with `status`, `traceId` and `requestId`. Transport failures (timeouts, connection errors) throw the underlying `fetch` errors. The default timeout is 10 seconds (`timeoutMs` to change it); pass `fetch` to supply a custom implementation.
63
+
64
+ ```typescript
65
+ import { TrustGuardAPIError } from "@neuraltrust/trustguard-sdk";
66
+
67
+ try {
68
+ await client.guard({ input: { prompt: "..." } });
69
+ } catch (err) {
70
+ if (err instanceof TrustGuardAPIError && err.status === 401) {
71
+ // bad API key
72
+ }
73
+ }
74
+ ```
75
+
76
+ ## Development
77
+
78
+ ```bash
79
+ npm install
80
+ npm run lint # tsc --noEmit
81
+ npm test # vitest
82
+ npm run build # tsup (ESM + CJS + d.ts)
83
+ ```
package/dist/index.cjs ADDED
@@ -0,0 +1,140 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ TrustGuard: () => TrustGuard,
24
+ TrustGuardAPIError: () => TrustGuardAPIError
25
+ });
26
+ module.exports = __toCommonJS(index_exports);
27
+
28
+ // src/errors.ts
29
+ var TrustGuardAPIError = class extends Error {
30
+ status;
31
+ traceId;
32
+ requestId;
33
+ constructor(status, message, traceId, requestId) {
34
+ super(`TrustGuard API error ${status}: ${message} (trace_id=${traceId}, request_id=${requestId})`);
35
+ this.name = "TrustGuardAPIError";
36
+ this.status = status;
37
+ this.traceId = traceId;
38
+ this.requestId = requestId;
39
+ }
40
+ };
41
+
42
+ // src/client.ts
43
+ var GUARD_PATH = "/v1/guard";
44
+ var DEFAULT_TIMEOUT_MS = 1e4;
45
+ var TrustGuard = class {
46
+ #baseUrl;
47
+ #apiKey;
48
+ #timeoutMs;
49
+ #fetch;
50
+ constructor(options) {
51
+ const baseUrl = options.baseUrl?.trim().replace(/\/+$/, "");
52
+ if (!baseUrl) {
53
+ throw new Error("TrustGuard: baseUrl is required");
54
+ }
55
+ if (!options.apiKey?.trim()) {
56
+ throw new Error("TrustGuard: apiKey is required");
57
+ }
58
+ this.#baseUrl = baseUrl;
59
+ this.#apiKey = options.apiKey;
60
+ this.#timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
61
+ this.#fetch = options.fetch ?? globalThis.fetch;
62
+ }
63
+ /**
64
+ * Evaluates the request against the policy attached to the API key and
65
+ * returns the verdict. Throws TrustGuardAPIError on non-2xx responses.
66
+ */
67
+ async guard(request) {
68
+ if (!request.input) {
69
+ throw new Error("TrustGuard: request input is required");
70
+ }
71
+ const response = await this.#fetch(this.#baseUrl + GUARD_PATH, {
72
+ method: "POST",
73
+ headers: {
74
+ Authorization: `Bearer ${this.#apiKey}`,
75
+ "Content-Type": "application/json"
76
+ },
77
+ body: JSON.stringify(serializeRequest(request)),
78
+ signal: AbortSignal.timeout(this.#timeoutMs)
79
+ });
80
+ const text = await response.text();
81
+ if (!response.ok) {
82
+ throw apiError(response.status, text);
83
+ }
84
+ return deserializeResponse(parseJson(text) ?? {});
85
+ }
86
+ };
87
+ function serializeRequest(request) {
88
+ const body = { input: request.input };
89
+ if (request.direction) body.direction = request.direction;
90
+ if (request.sessionId) body.session_id = request.sessionId;
91
+ if (request.consumerId) body.consumer_id = request.consumerId;
92
+ let metadata = request.metadata;
93
+ if (request.attachments?.length) {
94
+ metadata = { ...metadata, attachments: request.attachments.map(serializeAttachment) };
95
+ }
96
+ if (metadata && Object.keys(metadata).length > 0) body.metadata = metadata;
97
+ return body;
98
+ }
99
+ function serializeAttachment(attachment) {
100
+ const bytes = typeof attachment.data === "string" ? Buffer.from(attachment.data, "utf-8") : Buffer.from(attachment.data);
101
+ return {
102
+ filename: attachment.filename,
103
+ content_type: attachment.contentType,
104
+ data: bytes.toString("base64")
105
+ };
106
+ }
107
+ function deserializeResponse(wire) {
108
+ return {
109
+ isFlagged: wire.is_flagged ?? false,
110
+ transformedPayload: wire.transformed_payload ?? null,
111
+ findings: (wire.findings ?? []).map(
112
+ (f) => ({
113
+ detectionType: f.detection_type,
114
+ confidence: f.confidence,
115
+ ruleName: f.rule_name,
116
+ details: f.details
117
+ })
118
+ ),
119
+ traceId: wire.trace_id ?? "",
120
+ requestId: wire.request_id ?? ""
121
+ };
122
+ }
123
+ function apiError(status, text) {
124
+ const payload = parseJson(text);
125
+ const message = payload?.error || text.trim() || `HTTP ${status}`;
126
+ return new TrustGuardAPIError(status, message, payload?.trace_id ?? "", payload?.request_id ?? "");
127
+ }
128
+ function parseJson(text) {
129
+ try {
130
+ return JSON.parse(text);
131
+ } catch {
132
+ return void 0;
133
+ }
134
+ }
135
+ // Annotate the CommonJS export names for ESM import in node:
136
+ 0 && (module.exports = {
137
+ TrustGuard,
138
+ TrustGuardAPIError
139
+ });
140
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/client.ts"],"sourcesContent":["export { TrustGuard } from \"./client.js\";\nexport { TrustGuardAPIError } from \"./errors.js\";\nexport type {\n Attachment,\n Direction,\n Finding,\n GuardRequest,\n GuardResponse,\n TrustGuardOptions,\n} from \"./types.js\";\n","/**\n * A non-2xx response from the TrustGuard API, carrying the status code and the\n * correlation ids needed to chase the request in telemetry. Transport-level\n * failures (network errors, timeouts) are thrown as-is instead.\n */\nexport class TrustGuardAPIError extends Error {\n readonly status: number;\n readonly traceId: string;\n readonly requestId: string;\n\n constructor(status: number, message: string, traceId: string, requestId: string) {\n super(`TrustGuard API error ${status}: ${message} (trace_id=${traceId}, request_id=${requestId})`);\n this.name = \"TrustGuardAPIError\";\n this.status = status;\n this.traceId = traceId;\n this.requestId = requestId;\n }\n}\n","import { TrustGuardAPIError } from \"./errors.js\";\nimport type { Attachment, Finding, GuardRequest, GuardResponse, TrustGuardOptions } from \"./types.js\";\n\nconst GUARD_PATH = \"/v1/guard\";\nconst DEFAULT_TIMEOUT_MS = 10_000;\n\ninterface WireFinding {\n detection_type?: string;\n confidence?: number;\n rule_name?: string;\n details?: unknown;\n}\n\ninterface WireResponse {\n is_flagged?: boolean;\n transformed_payload?: Record<string, unknown> | null;\n findings?: WireFinding[];\n trace_id?: string;\n request_id?: string;\n}\n\n/** Client for the TrustGuard runtime guard API. */\nexport class TrustGuard {\n readonly #baseUrl: string;\n readonly #apiKey: string;\n readonly #timeoutMs: number;\n readonly #fetch: typeof fetch;\n\n constructor(options: TrustGuardOptions) {\n const baseUrl = options.baseUrl?.trim().replace(/\\/+$/, \"\");\n if (!baseUrl) {\n throw new Error(\"TrustGuard: baseUrl is required\");\n }\n if (!options.apiKey?.trim()) {\n throw new Error(\"TrustGuard: apiKey is required\");\n }\n this.#baseUrl = baseUrl;\n this.#apiKey = options.apiKey;\n this.#timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n this.#fetch = options.fetch ?? globalThis.fetch;\n }\n\n /**\n * Evaluates the request against the policy attached to the API key and\n * returns the verdict. Throws TrustGuardAPIError on non-2xx responses.\n */\n async guard(request: GuardRequest): Promise<GuardResponse> {\n if (!request.input) {\n throw new Error(\"TrustGuard: request input is required\");\n }\n\n const response = await this.#fetch(this.#baseUrl + GUARD_PATH, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.#apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(serializeRequest(request)),\n signal: AbortSignal.timeout(this.#timeoutMs),\n });\n\n const text = await response.text();\n if (!response.ok) {\n throw apiError(response.status, text);\n }\n\n return deserializeResponse(parseJson(text) ?? {});\n }\n}\n\n/** Maps the camelCase request onto the snake_case wire format, omitting empty optionals (the server rejects unknown top-level fields). */\nfunction serializeRequest(request: GuardRequest): Record<string, unknown> {\n const body: Record<string, unknown> = { input: request.input };\n if (request.direction) body.direction = request.direction;\n if (request.sessionId) body.session_id = request.sessionId;\n if (request.consumerId) body.consumer_id = request.consumerId;\n\n let metadata = request.metadata;\n if (request.attachments?.length) {\n metadata = { ...metadata, attachments: request.attachments.map(serializeAttachment) };\n }\n if (metadata && Object.keys(metadata).length > 0) body.metadata = metadata;\n return body;\n}\n\nfunction serializeAttachment(attachment: Attachment): Record<string, string> {\n const bytes =\n typeof attachment.data === \"string\" ? Buffer.from(attachment.data, \"utf-8\") : Buffer.from(attachment.data);\n return {\n filename: attachment.filename,\n content_type: attachment.contentType,\n data: bytes.toString(\"base64\"),\n };\n}\n\nfunction deserializeResponse(wire: WireResponse): GuardResponse {\n return {\n isFlagged: wire.is_flagged ?? false,\n transformedPayload: wire.transformed_payload ?? null,\n findings: (wire.findings ?? []).map(\n (f): Finding => ({\n detectionType: f.detection_type,\n confidence: f.confidence,\n ruleName: f.rule_name,\n details: f.details,\n }),\n ),\n traceId: wire.trace_id ?? \"\",\n requestId: wire.request_id ?? \"\",\n };\n}\n\nfunction apiError(status: number, text: string): TrustGuardAPIError {\n const payload = parseJson(text) as { error?: string; trace_id?: string; request_id?: string } | undefined;\n const message = payload?.error || text.trim() || `HTTP ${status}`;\n return new TrustGuardAPIError(status, message, payload?.trace_id ?? \"\", payload?.request_id ?? \"\");\n}\n\nfunction parseJson(text: string): WireResponse | undefined {\n try {\n return JSON.parse(text) as WireResponse;\n } catch {\n return undefined;\n }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,85 @@
1
+ /** Traffic side being evaluated; rules are scoped per side. Defaults to "input" server-side. */
2
+ type Direction = "input" | "output";
3
+ /** A document evaluated alongside the payload by file-aware plugins. */
4
+ interface Attachment {
5
+ filename: string;
6
+ contentType: string;
7
+ /** Raw file content; strings are encoded as UTF-8. Base64 encoding happens on the wire. */
8
+ data: Uint8Array | string;
9
+ }
10
+ /** Payload for a guard evaluation. */
11
+ interface GuardRequest {
12
+ /** The payload to evaluate. Required. */
13
+ input: Record<string, unknown>;
14
+ /** Traffic side; the server defaults to "input" when omitted. */
15
+ direction?: Direction;
16
+ /** Groups multi-turn traffic; the server synthesises one when omitted. */
17
+ sessionId?: string;
18
+ /** Identifies the end user on whose behalf the request runs. */
19
+ consumerId?: string;
20
+ /**
21
+ * Optional routing hints. Server-side key: `content_type` (overrides the
22
+ * payload content type, default application/json).
23
+ */
24
+ metadata?: Record<string, unknown>;
25
+ /** Documents to evaluate; folded into `metadata.attachments` on the wire. */
26
+ attachments?: Attachment[];
27
+ }
28
+ /** A single plugin's contribution to the guard response. */
29
+ interface Finding {
30
+ detectionType?: string;
31
+ confidence?: number;
32
+ ruleName?: string;
33
+ details?: unknown;
34
+ }
35
+ /**
36
+ * Verdict returned by POST /v1/guard. TrustGuard detects; the caller enforces:
37
+ * block when `isFlagged` is true.
38
+ */
39
+ interface GuardResponse {
40
+ /** The enforcement signal: true when the policy flagged the payload. */
41
+ isFlagged: boolean;
42
+ /** The payload as rewritten by in-flight masking, null when untouched. */
43
+ transformedPayload: Record<string, unknown> | null;
44
+ /** What every plugin in the policy chain reported. */
45
+ findings: Finding[];
46
+ /** Correlation ids for TrustGuard telemetry. */
47
+ traceId: string;
48
+ requestId: string;
49
+ }
50
+ /** Configuration for the TrustGuard client. */
51
+ interface TrustGuardOptions {
52
+ /** Base URL of the TrustGuard deployment, e.g. https://guard.neuraltrust.ai */
53
+ baseUrl: string;
54
+ /** Collector API key, sent as a Bearer token. */
55
+ apiKey: string;
56
+ /** Per-request timeout in milliseconds. Default 10000. */
57
+ timeoutMs?: number;
58
+ /** Custom fetch implementation (proxies, test doubles). Default globalThis.fetch. */
59
+ fetch?: typeof fetch;
60
+ }
61
+
62
+ /** Client for the TrustGuard runtime guard API. */
63
+ declare class TrustGuard {
64
+ #private;
65
+ constructor(options: TrustGuardOptions);
66
+ /**
67
+ * Evaluates the request against the policy attached to the API key and
68
+ * returns the verdict. Throws TrustGuardAPIError on non-2xx responses.
69
+ */
70
+ guard(request: GuardRequest): Promise<GuardResponse>;
71
+ }
72
+
73
+ /**
74
+ * A non-2xx response from the TrustGuard API, carrying the status code and the
75
+ * correlation ids needed to chase the request in telemetry. Transport-level
76
+ * failures (network errors, timeouts) are thrown as-is instead.
77
+ */
78
+ declare class TrustGuardAPIError extends Error {
79
+ readonly status: number;
80
+ readonly traceId: string;
81
+ readonly requestId: string;
82
+ constructor(status: number, message: string, traceId: string, requestId: string);
83
+ }
84
+
85
+ export { type Attachment, type Direction, type Finding, type GuardRequest, type GuardResponse, TrustGuard, TrustGuardAPIError, type TrustGuardOptions };
@@ -0,0 +1,85 @@
1
+ /** Traffic side being evaluated; rules are scoped per side. Defaults to "input" server-side. */
2
+ type Direction = "input" | "output";
3
+ /** A document evaluated alongside the payload by file-aware plugins. */
4
+ interface Attachment {
5
+ filename: string;
6
+ contentType: string;
7
+ /** Raw file content; strings are encoded as UTF-8. Base64 encoding happens on the wire. */
8
+ data: Uint8Array | string;
9
+ }
10
+ /** Payload for a guard evaluation. */
11
+ interface GuardRequest {
12
+ /** The payload to evaluate. Required. */
13
+ input: Record<string, unknown>;
14
+ /** Traffic side; the server defaults to "input" when omitted. */
15
+ direction?: Direction;
16
+ /** Groups multi-turn traffic; the server synthesises one when omitted. */
17
+ sessionId?: string;
18
+ /** Identifies the end user on whose behalf the request runs. */
19
+ consumerId?: string;
20
+ /**
21
+ * Optional routing hints. Server-side key: `content_type` (overrides the
22
+ * payload content type, default application/json).
23
+ */
24
+ metadata?: Record<string, unknown>;
25
+ /** Documents to evaluate; folded into `metadata.attachments` on the wire. */
26
+ attachments?: Attachment[];
27
+ }
28
+ /** A single plugin's contribution to the guard response. */
29
+ interface Finding {
30
+ detectionType?: string;
31
+ confidence?: number;
32
+ ruleName?: string;
33
+ details?: unknown;
34
+ }
35
+ /**
36
+ * Verdict returned by POST /v1/guard. TrustGuard detects; the caller enforces:
37
+ * block when `isFlagged` is true.
38
+ */
39
+ interface GuardResponse {
40
+ /** The enforcement signal: true when the policy flagged the payload. */
41
+ isFlagged: boolean;
42
+ /** The payload as rewritten by in-flight masking, null when untouched. */
43
+ transformedPayload: Record<string, unknown> | null;
44
+ /** What every plugin in the policy chain reported. */
45
+ findings: Finding[];
46
+ /** Correlation ids for TrustGuard telemetry. */
47
+ traceId: string;
48
+ requestId: string;
49
+ }
50
+ /** Configuration for the TrustGuard client. */
51
+ interface TrustGuardOptions {
52
+ /** Base URL of the TrustGuard deployment, e.g. https://guard.neuraltrust.ai */
53
+ baseUrl: string;
54
+ /** Collector API key, sent as a Bearer token. */
55
+ apiKey: string;
56
+ /** Per-request timeout in milliseconds. Default 10000. */
57
+ timeoutMs?: number;
58
+ /** Custom fetch implementation (proxies, test doubles). Default globalThis.fetch. */
59
+ fetch?: typeof fetch;
60
+ }
61
+
62
+ /** Client for the TrustGuard runtime guard API. */
63
+ declare class TrustGuard {
64
+ #private;
65
+ constructor(options: TrustGuardOptions);
66
+ /**
67
+ * Evaluates the request against the policy attached to the API key and
68
+ * returns the verdict. Throws TrustGuardAPIError on non-2xx responses.
69
+ */
70
+ guard(request: GuardRequest): Promise<GuardResponse>;
71
+ }
72
+
73
+ /**
74
+ * A non-2xx response from the TrustGuard API, carrying the status code and the
75
+ * correlation ids needed to chase the request in telemetry. Transport-level
76
+ * failures (network errors, timeouts) are thrown as-is instead.
77
+ */
78
+ declare class TrustGuardAPIError extends Error {
79
+ readonly status: number;
80
+ readonly traceId: string;
81
+ readonly requestId: string;
82
+ constructor(status: number, message: string, traceId: string, requestId: string);
83
+ }
84
+
85
+ export { type Attachment, type Direction, type Finding, type GuardRequest, type GuardResponse, TrustGuard, TrustGuardAPIError, type TrustGuardOptions };
package/dist/index.js ADDED
@@ -0,0 +1,112 @@
1
+ // src/errors.ts
2
+ var TrustGuardAPIError = class extends Error {
3
+ status;
4
+ traceId;
5
+ requestId;
6
+ constructor(status, message, traceId, requestId) {
7
+ super(`TrustGuard API error ${status}: ${message} (trace_id=${traceId}, request_id=${requestId})`);
8
+ this.name = "TrustGuardAPIError";
9
+ this.status = status;
10
+ this.traceId = traceId;
11
+ this.requestId = requestId;
12
+ }
13
+ };
14
+
15
+ // src/client.ts
16
+ var GUARD_PATH = "/v1/guard";
17
+ var DEFAULT_TIMEOUT_MS = 1e4;
18
+ var TrustGuard = class {
19
+ #baseUrl;
20
+ #apiKey;
21
+ #timeoutMs;
22
+ #fetch;
23
+ constructor(options) {
24
+ const baseUrl = options.baseUrl?.trim().replace(/\/+$/, "");
25
+ if (!baseUrl) {
26
+ throw new Error("TrustGuard: baseUrl is required");
27
+ }
28
+ if (!options.apiKey?.trim()) {
29
+ throw new Error("TrustGuard: apiKey is required");
30
+ }
31
+ this.#baseUrl = baseUrl;
32
+ this.#apiKey = options.apiKey;
33
+ this.#timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
34
+ this.#fetch = options.fetch ?? globalThis.fetch;
35
+ }
36
+ /**
37
+ * Evaluates the request against the policy attached to the API key and
38
+ * returns the verdict. Throws TrustGuardAPIError on non-2xx responses.
39
+ */
40
+ async guard(request) {
41
+ if (!request.input) {
42
+ throw new Error("TrustGuard: request input is required");
43
+ }
44
+ const response = await this.#fetch(this.#baseUrl + GUARD_PATH, {
45
+ method: "POST",
46
+ headers: {
47
+ Authorization: `Bearer ${this.#apiKey}`,
48
+ "Content-Type": "application/json"
49
+ },
50
+ body: JSON.stringify(serializeRequest(request)),
51
+ signal: AbortSignal.timeout(this.#timeoutMs)
52
+ });
53
+ const text = await response.text();
54
+ if (!response.ok) {
55
+ throw apiError(response.status, text);
56
+ }
57
+ return deserializeResponse(parseJson(text) ?? {});
58
+ }
59
+ };
60
+ function serializeRequest(request) {
61
+ const body = { input: request.input };
62
+ if (request.direction) body.direction = request.direction;
63
+ if (request.sessionId) body.session_id = request.sessionId;
64
+ if (request.consumerId) body.consumer_id = request.consumerId;
65
+ let metadata = request.metadata;
66
+ if (request.attachments?.length) {
67
+ metadata = { ...metadata, attachments: request.attachments.map(serializeAttachment) };
68
+ }
69
+ if (metadata && Object.keys(metadata).length > 0) body.metadata = metadata;
70
+ return body;
71
+ }
72
+ function serializeAttachment(attachment) {
73
+ const bytes = typeof attachment.data === "string" ? Buffer.from(attachment.data, "utf-8") : Buffer.from(attachment.data);
74
+ return {
75
+ filename: attachment.filename,
76
+ content_type: attachment.contentType,
77
+ data: bytes.toString("base64")
78
+ };
79
+ }
80
+ function deserializeResponse(wire) {
81
+ return {
82
+ isFlagged: wire.is_flagged ?? false,
83
+ transformedPayload: wire.transformed_payload ?? null,
84
+ findings: (wire.findings ?? []).map(
85
+ (f) => ({
86
+ detectionType: f.detection_type,
87
+ confidence: f.confidence,
88
+ ruleName: f.rule_name,
89
+ details: f.details
90
+ })
91
+ ),
92
+ traceId: wire.trace_id ?? "",
93
+ requestId: wire.request_id ?? ""
94
+ };
95
+ }
96
+ function apiError(status, text) {
97
+ const payload = parseJson(text);
98
+ const message = payload?.error || text.trim() || `HTTP ${status}`;
99
+ return new TrustGuardAPIError(status, message, payload?.trace_id ?? "", payload?.request_id ?? "");
100
+ }
101
+ function parseJson(text) {
102
+ try {
103
+ return JSON.parse(text);
104
+ } catch {
105
+ return void 0;
106
+ }
107
+ }
108
+ export {
109
+ TrustGuard,
110
+ TrustGuardAPIError
111
+ };
112
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/errors.ts","../src/client.ts"],"sourcesContent":["/**\n * A non-2xx response from the TrustGuard API, carrying the status code and the\n * correlation ids needed to chase the request in telemetry. Transport-level\n * failures (network errors, timeouts) are thrown as-is instead.\n */\nexport class TrustGuardAPIError extends Error {\n readonly status: number;\n readonly traceId: string;\n readonly requestId: string;\n\n constructor(status: number, message: string, traceId: string, requestId: string) {\n super(`TrustGuard API error ${status}: ${message} (trace_id=${traceId}, request_id=${requestId})`);\n this.name = \"TrustGuardAPIError\";\n this.status = status;\n this.traceId = traceId;\n this.requestId = requestId;\n }\n}\n","import { TrustGuardAPIError } from \"./errors.js\";\nimport type { Attachment, Finding, GuardRequest, GuardResponse, TrustGuardOptions } from \"./types.js\";\n\nconst GUARD_PATH = \"/v1/guard\";\nconst DEFAULT_TIMEOUT_MS = 10_000;\n\ninterface WireFinding {\n detection_type?: string;\n confidence?: number;\n rule_name?: string;\n details?: unknown;\n}\n\ninterface WireResponse {\n is_flagged?: boolean;\n transformed_payload?: Record<string, unknown> | null;\n findings?: WireFinding[];\n trace_id?: string;\n request_id?: string;\n}\n\n/** Client for the TrustGuard runtime guard API. */\nexport class TrustGuard {\n readonly #baseUrl: string;\n readonly #apiKey: string;\n readonly #timeoutMs: number;\n readonly #fetch: typeof fetch;\n\n constructor(options: TrustGuardOptions) {\n const baseUrl = options.baseUrl?.trim().replace(/\\/+$/, \"\");\n if (!baseUrl) {\n throw new Error(\"TrustGuard: baseUrl is required\");\n }\n if (!options.apiKey?.trim()) {\n throw new Error(\"TrustGuard: apiKey is required\");\n }\n this.#baseUrl = baseUrl;\n this.#apiKey = options.apiKey;\n this.#timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n this.#fetch = options.fetch ?? globalThis.fetch;\n }\n\n /**\n * Evaluates the request against the policy attached to the API key and\n * returns the verdict. Throws TrustGuardAPIError on non-2xx responses.\n */\n async guard(request: GuardRequest): Promise<GuardResponse> {\n if (!request.input) {\n throw new Error(\"TrustGuard: request input is required\");\n }\n\n const response = await this.#fetch(this.#baseUrl + GUARD_PATH, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.#apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(serializeRequest(request)),\n signal: AbortSignal.timeout(this.#timeoutMs),\n });\n\n const text = await response.text();\n if (!response.ok) {\n throw apiError(response.status, text);\n }\n\n return deserializeResponse(parseJson(text) ?? {});\n }\n}\n\n/** Maps the camelCase request onto the snake_case wire format, omitting empty optionals (the server rejects unknown top-level fields). */\nfunction serializeRequest(request: GuardRequest): Record<string, unknown> {\n const body: Record<string, unknown> = { input: request.input };\n if (request.direction) body.direction = request.direction;\n if (request.sessionId) body.session_id = request.sessionId;\n if (request.consumerId) body.consumer_id = request.consumerId;\n\n let metadata = request.metadata;\n if (request.attachments?.length) {\n metadata = { ...metadata, attachments: request.attachments.map(serializeAttachment) };\n }\n if (metadata && Object.keys(metadata).length > 0) body.metadata = metadata;\n return body;\n}\n\nfunction serializeAttachment(attachment: Attachment): Record<string, string> {\n const bytes =\n typeof attachment.data === \"string\" ? Buffer.from(attachment.data, \"utf-8\") : Buffer.from(attachment.data);\n return {\n filename: attachment.filename,\n content_type: attachment.contentType,\n data: bytes.toString(\"base64\"),\n };\n}\n\nfunction deserializeResponse(wire: WireResponse): GuardResponse {\n return {\n isFlagged: wire.is_flagged ?? false,\n transformedPayload: wire.transformed_payload ?? null,\n findings: (wire.findings ?? []).map(\n (f): Finding => ({\n detectionType: f.detection_type,\n confidence: f.confidence,\n ruleName: f.rule_name,\n details: f.details,\n }),\n ),\n traceId: wire.trace_id ?? \"\",\n requestId: wire.request_id ?? \"\",\n };\n}\n\nfunction apiError(status: number, text: string): TrustGuardAPIError {\n const payload = parseJson(text) as { error?: string; trace_id?: string; request_id?: string } | undefined;\n const message = payload?.error || text.trim() || `HTTP ${status}`;\n return new TrustGuardAPIError(status, message, payload?.trace_id ?? \"\", payload?.request_id ?? \"\");\n}\n\nfunction parseJson(text: string): WireResponse | undefined {\n try {\n return JSON.parse(text) as WireResponse;\n } catch {\n return undefined;\n }\n}\n"],"mappings":"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","names":[]}
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@neuraltrust/trustguard-sdk",
3
+ "version": "0.1.0",
4
+ "description": "Official Node.js SDK for the TrustGuard runtime guard API (POST /v1/guard)",
5
+ "license": "MIT",
6
+ "author": "NeuralTrust",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/NeuralTrust/trustguard-sdk.git",
10
+ "directory": "node"
11
+ },
12
+ "keywords": [
13
+ "neuraltrust",
14
+ "trustguard",
15
+ "guardrails",
16
+ "llm-security",
17
+ "ai-firewall"
18
+ ],
19
+ "type": "module",
20
+ "main": "./dist/index.cjs",
21
+ "module": "./dist/index.js",
22
+ "types": "./dist/index.d.ts",
23
+ "exports": {
24
+ ".": {
25
+ "types": "./dist/index.d.ts",
26
+ "import": "./dist/index.js",
27
+ "require": "./dist/index.cjs"
28
+ }
29
+ },
30
+ "files": [
31
+ "dist",
32
+ "README.md"
33
+ ],
34
+ "engines": {
35
+ "node": ">=18"
36
+ },
37
+ "scripts": {
38
+ "build": "tsup",
39
+ "lint": "tsc --noEmit",
40
+ "test": "vitest run",
41
+ "prepublishOnly": "npm run lint && npm run test && npm run build"
42
+ },
43
+ "devDependencies": {
44
+ "@types/node": "^22.10.2",
45
+ "tsup": "^8.3.5",
46
+ "typescript": "^5.7.2",
47
+ "vitest": "^2.1.8"
48
+ }
49
+ }