Decisions as a Service
Sign Up Now →Sign Up Now

JavaScript and TypeScript

helixor-sdk-gen 0.1.0 generates typed TypeScript clients from OpenAPI contracts. The package compiles to JavaScript for Node and browser consumers; there is no separate JavaScript emitter. For in-process decision execution, an embedded TypeScript SDK remains planned.

OpenAPI generation availablePlanned: embedded SDKNode 18+

Generated OpenAPI client#

Select typescript in the SDK configuration. The generated package includes typed models and operations, configurable authentication, request and response validation, retry and timeout policy, and language-neutral operation contracts. Build output is ordinary JavaScript with TypeScript declarations.

package_name: example-sdk
package_version: 0.1.0
languages: [typescript]
base_url: https://api.example.test

Generation support does not mean a package has already been published to npm. Use the release manifest for the API you are integrating with to determine package availability. See the language capability matrix.

Decision Runtime 0.3.1#

An embedded TypeScript SDK generated from the pack manifest is Planned. Until it ships, TypeScript and Node callers use the local service:

  1. Start the service

    Run helixor-pack serve for a compiled pack (see CLI), or start_server from Python for the built-in example pack. It binds loopback by default. If callers on other hosts need it, give it a bearer token (HELIXOR_SERVICE_TOKEN) and send Authorization: Bearer from your client.

  2. Call it with fetch

    Node 18 and later include fetch, so you need no dependencies.

Types#

These interfaces mirror the JSON the service returns. See the Python reference for what each field means.

export interface RegulatoryTrigger {
  rule_id: string;
  law: string;
  severity: "FATAL" | "WARNING";
  matched_items: string[]; // raw sensitive values: never log
}

export interface CounterfactualRemedy {
  clean_text: string;
  redactions_count: number;
  redacted_categories: string[];
}

export interface DecisionResult {
  pack_id: string;
  action: string;
  invariants_passed: boolean;
  reason: string;
  triggers: RegulatoryTrigger[];
  remedy: CounterfactualRemedy;
  latency_us: number;
  tokens_spent: number;
  egress_bytes: number;
  receipt_hash: string;
  license_status: string | null;
  active_key_id: string | null;
  active_epoch: number | null;
  edition: string;
  upgrade_notice: string | null;
}

export interface StreamChunkResult {
  chunk_index: number;
  text: string;
  is_final: boolean;
  redacted: boolean;
  blocked: boolean;
  action: string;
  reason: string | null;
  triggers: RegulatoryTrigger[];
}

Evaluate a payload#

import type { DecisionResult } from "./helixor-types";

const BASE_URL = process.env.HELIXOR_SERVICE_URL ?? "http://127.0.0.1:18734";

export async function evaluate(text: string): Promise<DecisionResult> {
  const res = await fetch(`${BASE_URL}/v1/evaluate`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ text }),
  });
  if (!res.ok) {
    throw new Error(`evaluate failed: HTTP ${res.status} ${await res.text()}`);
  }
  return (await res.json()) as DecisionResult;
}

const result = await evaluate("Customer card 4111-1111-1111-1111, contact jane@example.com");
const mustBlock = result.triggers.some((t) => t.severity === "FATAL");
if (mustBlock) {
  console.log("blocked", result.action, result.receipt_hash);
} else {
  console.log("forward", result.remedy.clean_text);
}

HELIXOR_SERVICE_URL is a name used by this example, not a variable the runtime reads. Fail closed: if the service is unreachable or returns an error, do not forward the payload.

Read an SSE stream#

POST /v1/evaluate/stream takes the whole text and splits it on the server, so it suits testing more than live model output. For live tokens, keep the /v1/ws/decision WebSocket open and send stream_chunk messages. Both are described in the HTTP API reference.

import type { StreamChunkResult } from "./helixor-types";

const res = await fetch(`${BASE_URL}/v1/evaluate/stream`, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ text: "Call (415) 555-0100 today.", chunk_size: 8 }),
});
const reader = res.body!.pipeThrough(new TextDecoderStream()).getReader();
let buffer = "";
for (;;) {
  const { value, done } = await reader.read();
  if (done) break;
  buffer += value;
  let sep;
  while ((sep = buffer.indexOf("\n\n")) >= 0) {
    const line = buffer.slice(0, sep).trim();
    buffer = buffer.slice(sep + 2);
    if (!line.startsWith("data: ")) continue;
    const chunk = JSON.parse(line.slice(6)) as StreamChunkResult;
    if (chunk.blocked) throw new Error(`stream blocked: ${chunk.action}`);
    process.stdout.write(chunk.text);
  }
}