npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@haitch-diy/sdk

v0.0.1

Published

Official TypeScript SDK for the Haitch developer API — AI-native hardware product creation across System, CAD, Canvas, Firmware, and Electronics workspaces, plus Store, Partners, files, artifacts, and BOM sourcing.

Readme

Haitch TypeScript SDK

@haitch-diy/sdk is the official TypeScript SDK for the Haitch developer API — an AI-native platform for hardware product creation across System, CAD, Canvas, Firmware, and Electronics workspaces, with Store, Partners, files, artifacts, and BOM sourcing.

Preview (0.x). The public contract is stabilizing; minor versions may include breaking changes until a 1.0 release. See VERSIONING.md.

Install

npm install @haitch-diy/sdk

Create a developer API key from your Haitch developer dashboard, then keep it in a server-side environment variable. The SDK is for server-side use only — never embed an API key in a browser or any client the end user controls.

import { HaitchClient } from "@haitch-diy/sdk";

// baseUrl defaults to the hosted Haitch API origin; override only if directed to.
const client = new HaitchClient({
  apiKey: process.env.HAITCH_API_KEY!,
});

const response = await client.responses.create({
  workspace: "system",
  type: "validate_system",
  input: {
    graph: {
      product_id: "greenhouse_monitor_quickstart",
      product_name: "Connected Greenhouse Monitor",
      root_assembly: {
        id: "asm_greenhouse_monitor",
        name: "Connected Greenhouse Monitor",
        children: [
          { id: "controller_pcb", name: "ESP32 Sensor Controller PCB" },
          { id: "environment_sensor", name: "Temperature and Humidity Sensor" },
        ],
      },
    },
  },
});

for await (const event of client.responses.stream(response.id)) {
  console.log(event.type, event.message);
  if (event.type === "response.artifact.created") {
    console.log(event.payload?.artifact);
  }
}

Firmware, CAD, Canvas, and Electronics response routes are project-bound. Use projects.createAndGenerate(...) when you want the API to create a fresh workspace first, or pass project_id when generating inside an existing workspace:

const generatedFirmware = await client.projects.createAndGenerate("firmware", {
  project: { name: "ESP32 environmental sensor" },
  description: "Generate firmware for an ESP32 environmental sensor",
});

await client.firmware.create("Add a low-power telemetry mode", {
  project_id: generatedFirmware.project.id,
});

Imported STEP workflows

Upload a model/step file with the existing Files API, then pass its file_id to the CAD STEP helpers. These request and response fields intentionally preserve the public API's snake_case contract.

const file = await client.files.create({
  purpose: "cad_import",
  filename: "reference.step",
  content_type: "model/step",
  content_base64: stepFileBase64,
});

const imported = await client.cad.importStep({ file_id: file.id });
const digest = await client.cad.stepImports.digest({
  analysis_storage_id: imported.analysis_storage_id,
});
const mode = await client.cad.classifyInstruction({
  user_instruction: "Create a screw-on enclosure around this reference part.",
});

if (mode.mode === "derive") {
  const derived = await client.cad.derivedParts.create({
    source_step_storage_id: imported.step_storage_id,
    analysis_storage_id: imported.analysis_storage_id,
    user_instruction: "Create a screw-on enclosure around this reference part.",
    design_intent: { wall_thickness: 2, clearance: 0.5, closure: "screw_lid" },
  });
  console.log(derived.step_url, digest.reference_digest);
}

Generated OpenAPI contract helpers are exported from the package. They are regenerated from openapi/haitch-v1.yaml and are useful for request builders, docs checks, and compile-time coverage assertions:

import {
  OPENAPI_PATHS,
  OPENAPI_WORKSPACES,
  type HaitchOpenApiPath,
  type HaitchOpenApiWorkspace,
} from "@haitch-diy/sdk";

const path: HaitchOpenApiPath = "/v1/responses/{responseId}/events";
const workspace: HaitchOpenApiWorkspace = OPENAPI_WORKSPACES[0];
console.log(path, workspace, OPENAPI_PATHS.length);

A runnable quickstart ships in the package at examples/quickstart.mjs. It validates a compact System graph response, reads events, and can optionally wait for the terminal state plus temporary artifact downloads:

HAITCH_API_KEY=hd_test_... node quickstart.mjs --wait-terminal --require-artifacts

Completed projectless System quickstart responses register downloadable response artifacts from their output graph/document/validation payloads, so --require-artifacts verifies the same artifact-download contract as project-backed workspace responses.

Project helpers let API-key owners inspect and manage existing workspaces before starting generation jobs that need a concrete project id:

const allCadProjects = await client.projects.list({ workspace: "cad", limit: 20 });
const firmwareProjects = await client.projects.listForWorkspace("firmware", { limit: 20 });
const createdProject = await client.projects.create("cad", { name: "New enclosure" });
const generatedProject = await client.projects.createAndGenerate("cad", {
  project: { name: "Generated enclosure" },
  prompt: "Create a compact electronics enclosure",
});
const project = await client.projects.retrieve("cad", "project_123", { include_state: true });
const updateResult = await client.projects.update("cad", "project_123", { name: "Updated enclosure" });
const rebuild = await client.projects.update("firmware", "firmware_project_123", { files: firmwareFiles });
if (rebuild.object === "project_rebuild") {
  await client.projects.retrieveRebuild("firmware", "firmware_project_123", rebuild.id);
}
await client.projects.delete("cad", "project_123");
console.log(allCadProjects.data.length, firmwareProjects.data.length, createdProject.id, generatedProject.response.id, project.workspace, updateResult.object);

These helpers call /v1/projects, /v1/workspaces/{workspace}/projects, and /v1/workspaces/{workspace}/projects/{projectId} for System, CAD, Canvas, Firmware, and Electronics. Reads require the corresponding {workspace}:read scope; creation requires {workspace}:create and creates a project container without enqueueing generation; createAndGenerate() requires responses:create plus {workspace}:create, rejects preexisting project ids, and returns { project, response }; updates accept {workspace}:write or existing {workspace}:create generation scope, and deletes require explicit {workspace}:delete plus owner access enforced by the public API. Metadata-only updates return HaitchProject. Source-code patches return ProjectRebuild and start the same rebuild/promotion flow used by the web workspace: Firmware files, Electronics tscircuitSource / source, and CAD currentData.scriptCode / currentData.stepCadCode patches. CAD source patches run headlessly for three.js, manifold, JSCAD, replicad.js, opencascade.js, and rhino3dm.js; cadquery remains supported through the Python compile path. Poll rebuilds with client.projects.retrieveRebuild(...).

System graph authoring is operation-based and versioned. Generic projects.update("system", ...) graph replacement remains a compatibility/promotion path for existing integrations; it is not the recommended authoring surface. Retrieve the project with include_state: true to obtain its graph and version, preview a batch, then apply it. Protected or destructive changes must reuse the returned preview id:

const project = await client.projects.retrieve("system", "system_123", { include_state: true });
const operations = [
  {
    op: "add_node" as const,
    parentId: "asm_controls",
    node: { name: "Temperature sensor", type: "part_offtheshelf", quantity: 1 },
  },
  {
    op: "upsert_node_parameter" as const,
    nodeId: "prt_sensor",
    key: "measurement_range",
    value: { value: 125, unit: "C" },
  },
];
const preview = await client.system.graph.previewOperations("system_123", {
  base_version: project.version!,
  operations,
});
const result = await client.system.graph.applyOperations("system_123", {
  base_version: project.version!,
  operations,
  idempotency_key: crypto.randomUUID(),
  ...(preview.requires_confirmation
    ? { confirmation: { preview_id: preview.preview_id } }
    : {}),
});
console.log(result.version, result.diff);

The SDK exposes discriminated SystemGraphOperation request types and typed preview/result responses. The shorter preview() and apply() aliases preserve the same semantics. Conflict and confirmation details remain structured:

try {
  await client.system.graph.applyOperations("system_123", request, {
    idempotencyKey: "graph-edit-7-sensor",
  });
} catch (error) {
  if (error instanceof HaitchApiError && error.code === "system_graph_version_conflict") {
    const currentVersion = error.details?.currentVersion;
    // Refetch, review the intervening change, and create a new preview explicitly.
    console.log("Current graph version", currentVersion);
  }
  if (error instanceof HaitchApiError && error.code === "system_graph_protected_change_requires_confirmation") {
    // Preview the exact batch and resubmit it with confirmation.preview_id.
  }
}

Workspace helpers are thin wrappers over the dedicated workspace endpoints:

await client.system.create("Design a wearable air-quality monitor");
await client.system.validateGraph({
  mode: "strict",
  graph: { root_assembly: { id: "root", children: [] } },
});
await client.system.createHandoffPackage({
  graph: { root_assembly: { id: "root", children: [] } },
});
await client.system.exportBundle({
  project_name: "Sensor",
  graph_version: 1,
  graph: { root_assembly: { id: "root", children: [] } },
});
await client.system.sourceParts("system_123", {
  part: { id: "part_1", name: "Humidity sensor" },
  quality_mode: "high_confidence",
});
await client.system.listSources("system_123", { limit: 25 });
await client.system.saveSource("system_123", {
  part_id: "part_1",
  source: { vendor: "Digi-Key", url: "https://example.test/part" },
  selected_by_action: "developer_api",
});
await client.system.submitSourceFeedback("system_123", {
  action: "selected",
  part: { id: "part_1", name: "Humidity sensor" },
  selected: { vendor: "Digi-Key" },
  reason: "best availability",
});
const cadResponse = await client.cad.create("Revise the enclosure as a selectable multi-part assembly", {
  project_id: "project_123",
});
if (cadResponse.output?.assemblyManifest) {
  console.log(cadResponse.output.engineRouting?.tradeOffs);
  console.log(cadResponse.output.fidelityScore, cadResponse.output.referenceKind);
  console.log(cadResponse.output.assemblyManifest.parts);
}
await client.canvas.create({
  prompt: "Create product listing media",
  mode: "design",
}, { project_id: "canvas_123" });
await client.canvas.create({
  prompt: "Create an exploded assembly view",
  mode: "exploded_view",
  sourceArtifactId: "artifact_reference_image",
}, { project_id: "canvas_123" });
await client.canvas.generate3dModel({
  input: {
    request_type: 3,
    generation_mode: "image",
    model: "hitem3dv1.5",
    format: "glb",
    sourceArtifactId: "artifact_reference_image",
  },
});
await client.canvas.generateTextureModel({
  input: {
    request_type: 3,
    generation_mode: "image",
    model: "hitem3dv2.0",
    format: "glb",
    pbr: 1,
    sourceArtifactId: "artifact_reference_image",
    mesh_url: "https://files.example/reference-mesh.glb",
  },
});
await client.firmware.create("Generate ESP32 firmware");
await client.electronics.create("Generate the PCB", { project_id: "electronics_123" });
await client.electronics.validatePcb("Run PCB validation", { project_id: "electronics_123" });
const electronicsCompile = await client.electronics.compile({
  project_id: "electronics_123",
  source: "export default () => <board width={20} height={20} />",
});
await client.electronics.retrieveCompile(electronicsCompile.id, { from: 0 });
await client.electronics.logs(electronicsCompile.id);
await client.electronics.searchComponents({ q: "esp32", limit: 5 });
await client.electronics.resolveComponent({
  component_ref: "U1",
  chip_name: "ESP32-WROOM",
});
await client.electronics.sourceParts("electronics_123", {
  part: { id: "U2", name: "Humidity sensor" },
  source: "jlcpcb",
  limit: 5,
});
await client.electronics.listSources("electronics_123", { limit: 25 });
await client.electronics.saveSource("electronics_123", {
  part_id: "U2",
  part_name: "Humidity sensor",
  source: { vendor: "LCSC", lcsc: "C123" },
  selected_by_action: "developer_api",
});
await client.electronics.artifacts("electronics_123", { stage: "pcb", limit: 20 });
await client.electronics.validationReports("electronics_123", { stage: "pcb" });
await client.electronics.bom("electronics_123");

// Workspace-neutral BOM artifact and sourcing helpers
const bom = await client.bom.retrieve("cad", "cad_project_123");
const candidates = await client.bom.searchSources("cad", "cad_project_123", { row: bom.rows[0] });
await client.bom.saveSource("cad", "cad_project_123", {
  part_id: String(bom.rows[0].id),
  row_stable_key: String(bom.rows[0].stable_key),
  bom_revision_id: bom.revision_id,
  source: candidates.source as Record<string, unknown>,
});
await client.electronics.manufacturingPackages("electronics_123");

Those helpers call /v1/system/graphs, /v1/system/graphs/validate, /v1/system/graphs/handoff-package, /v1/system/graphs/export-bundle, /v1/system/projects/{projectId}/sources/search, /v1/system/projects/{projectId}/sources, /v1/system/projects/{projectId}/sources/feedback, /v1/cad/generations, /v1/canvas/designs, /v1/canvas/models, /v1/firmware/builds, and /v1/electronics/designs while client.responses.create() remains available for the generic /v1/responses facade. Canvas design responses support executable input.mode values design, sketch, engineering_drawing, exploded_view, video, cmf, multiview, design_story, and engineering_analysis; every mode except design requires a source image, image group, or attachment reference. Hitem3D geometry and textured model generation use client.canvas.generate3dModel and client.canvas.generateTextureModel; model-task examples pass provider-valid model enums explicitly (hitem3dv1.5 for geometry and hitem3dv2.0 for the PBR texture path) so executable smoke runs match the server-side validation contract. Product design suggestions and CMF extraction are not separate /v1/canvas/designs modes in this SDK; use the supported design or cmf modes, or the application-specific internal tools when you need those analysis-only workflows. Electronics compile helpers call /v1/electronics/compiles plus status and log endpoints. Electronics component search and resolution call /v1/electronics/components/search and /v1/electronics/components/resolve. Electronics project deliverable helpers read persisted artifacts, validation reports, BOM rows, and manufacturing packages from /v1/electronics/projects/{projectId}/*. First-turn Electronics generation should use client.electronics.create(...) without a type, or with type: "generate_schematic", so server-side confirmation gates are auto-confirmed for API callers; generate_pcb is a later-stage command once source artifacts exist. Electronics BOM source helpers call /v1/electronics/projects/{projectId}/sources/search and /v1/electronics/projects/{projectId}/sources.

Runtime capabilities can be discovered with:

const capabilities = await client.capabilities.retrieve();
console.log(capabilities.workspaces.electronics.response_endpoint);

The Canvas mode example dry-runs payloads by default and only calls the API when --execute is passed:

node canvas-modes.mjs --help
HAITCH_API_KEY=hd_test_... node canvas-modes.mjs --execute --canvas-id canvas_123 --source-artifact-id artifact_123

Files and artifact metadata are available through the same API-key client:

const file = await client.files.create({
  purpose: "reference_image",
  filename: "reference.png",
  content_type: "image/png",
  content_base64: "base64-content",
});
const files = await client.files.list({ purpose: "reference_image", limit: 20 });

const registered = await client.artifacts.register({
  file_id: file.id,
  response_id: "promoted:example",
  workspace: "canvas",
  type: "image",
  metadata: { source: "public_project_promotion" },
});
const artifact = await client.artifacts.download("artifact_123");
console.log(file.url, files.data.length, registered.id, artifact.download_url);

const artifacts = await client.artifacts.list({ workspace: "cad", limit: 20 });
console.log(artifacts.data.map((item) => item.filename));

Firmware compile helpers are available when you already have an owned Firmware project and source files:

const compile = await client.firmware.compile({
  project_id: "firmware_project_123",
  compiler: "platformio",
  board_config: {
    platform: "esp32",
    board: "esp32dev",
    framework: "arduino",
  },
  files: [
    {
      path: "src/main.cpp",
      content: "void setup(){} void loop(){}",
    },
  ],
});

const status = await client.firmware.retrieveCompile(compile.id, {
  include_logs: true,
  from: 0,
});
const binary = await client.firmware.binary(compile.id);
console.log(status.status, binary.download_url);

const boards = await client.firmware.boards.list({ platform: "esp32" });
const platforms = await client.firmware.platforms.list();
const libraries = await client.firmware.libraries.search({
  query: "adafruit",
  source: "platformio",
  limit: 5,
});
console.log(boards.data.length, platforms.platforms.length, libraries.data.length);

Completed responses also include registered storage-backed artifacts when the workflow output contains durable storage ids:

const response = await client.responses.retrieve("resp_123");
for (const artifact of response.artifacts ?? []) {
  console.log(artifact.id, artifact.type, artifact.filename);
}

Store and Partner helpers are also available:

await client.store.products.list({ limit: 20 });
await client.partners.listDirectory({ sort: "match", limit: 10 });

const product = await client.store.products.createDraft({
  project_id: "project_123",
  title: "Sensor kit",
  description: "A compact environmental sensor kit.",
  images: ["storage_image_id"],
});
await client.store.products.update(String(product.data._id), {
  description: "Updated product description.",
});
await client.store.products.submit(String(product.data._id));
await client.store.products.createInterestLead(String(product.data._id), {
  email: "[email protected]",
  source: "launch_page",
});
await client.store.leads.list({ product_id: String(product.data._id), limit: 20 });
await client.store.orders.list({ limit: 20 });
await client.store.reservations.list({ product_id: String(product.data._id), limit: 20 });
await client.store.metrics.retrieve();

const partnerRequest = await client.partners.createRequest({
  source_workspace: "electronics",
  source_project_id: "electronics_123",
  title: "Prototype PCB",
  request_type: "manufacturing_rfq",
  requested_deliverables: ["PCB assembly quote"],
});
await client.partners.requests.list({ limit: 20 });
await client.partners.inviteToRequest(String(partnerRequest.data.request._id), {
  partner_id: "partner_123",
  invitation_kind: "rfq",
  message: "Can you quote this prototype build?",
});
await client.partners.respondToInvitation("invitation_123", {
  response: "accepted",
});

const partnerProfile = await client.partners.profiles.createDraft({
  name: "Board House",
  partner_types: ["manufacturer"],
});
await client.partners.profiles.update(String(partnerProfile.data._id), {
  service_summary: "Prototype PCB assembly and enclosure quoting.",
});
await client.partners.profiles.submit(String(partnerProfile.data._id));

await client.partners.requests.addArtifact(String(partnerRequest.data.request._id), {
  source_workspace: "electronics",
  source_project_id: "electronics_123",
  artifact_type: "bom",
  storage_id: "storage_id",
});
await client.partners.rooms.list({ limit: 10 });
await client.partners.rooms.sendMessage("room_123", {
  text: "Thanks for the quote.",
});
await client.partners.rooms.submitFeedback("room_123", {
  ratings: { overall: 5, communication: 5 },
  reported_outcome: "quote_received",
  would_use_again: true,
});
await client.partners.rooms.close("room_123");
await client.partners.reportProfile("partner_123", {
  reason: "misleading_profile",
});
await client.partners.rooms.report("room_123", {
  reason: "spam",
  message_id: "message_123",
});

Store and Partner write helpers use the same public API validation as raw HTTP and MCP calls. Oversized or deeply nested commercialization payloads are rejected before they are persisted; Partner artifact and room attachment URLs must be HTTP(S), and room attachments must include a storage id or URL.

Usage can be retrieved without consuming quota:

const usage = await client.usage.retrieve();
console.log(usage.count, usage.remaining, usage.limit);

This package intentionally covers only supported endpoints. Store checkout, payout, and settlement operations plus Partner approval/moderation are not part of the first-launch contract. The hosted MCP connection renders shared project, response, artifact, and BOM views over the same public API this SDK uses, so no SDK helper is needed for view state.