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

struai

v2.6.1

Published

StruAI Drawing Analysis SDK - AI-powered construction drawing analysis

Readme

StruAI JavaScript SDK

Official JavaScript/TypeScript SDK for the StruAI Drawing Analysis API.

  • Endpoint + response-shape reference: ../docs/HTTP_ENDPOINTS.md

Installation

npm install struai

Environment

export STRUAI_API_KEY=your_api_key
# Optional: defaults to https://api.stru.ai (SDK appends /v1 automatically)
export STRUAI_BASE_URL=https://api.stru.ai

Quick Start

import { StruAI } from 'struai';

const client = new StruAI({
  apiKey: process.env.STRUAI_API_KEY!,
  baseUrl: process.env.STRUAI_BASE_URL, // optional
  timeout: 60_000, // optional
});

const drawing = await client.drawings.analyze('/absolute/path/to/structural.pdf', { page: 12 });
const project = await client.projects.create({ name: 'Building A', description: 'Structural set' });
const jobOrBatch = await project.sheets.add(null, {
  page: 12,
  fileHash: await client.drawings.computeFileHash('/absolute/path/to/structural.pdf'),
});

const search = await project.docquery.search('beam connection', { limit: 5 });
console.log(search.hits.length);

const review = await client.reviews.create({
  fileHash: await client.drawings.computeFileHash('/absolute/path/to/structural.pdf'),
  pages: '12,13',
  projectIds: [project.id],
  customInstructions: 'Focus on cross-sheet coordination.',
});
const finalReview = await review.wait({ timeoutMs: 900_000, pollIntervalMs: 5_000 });
console.log(finalReview.status, (await review.issues()).length);

const customReview = await client.reviews.create({
  fileHash: await client.drawings.computeFileHash('/absolute/path/to/structural.pdf'),
  pages: '13',
  projectIds: [project.id],
  scout: 'Route broken references to Cross Reference Checker and buildability gaps to Constructability Reviewer.',
  specialistsCommon: 'All specialists should stay grounded in DocQuery/search-docs evidence.',
  specialists: [
    { name: 'Cross Reference Checker', instructions: 'Trace all detail, section, and sheet references.' },
    { name: 'Constructability Reviewer', instructions: 'Focus on missing dimensions and buildability gaps.' },
  ],
});
console.log(customReview.id);

const uploadReview = await client.reviews.create({
  file: '/absolute/path/to/structural.pdf',
  pages: 12,
});
console.log(uploadReview.id);

Real Workflow Examples

npm install
npm run build

# Drawings-only flow
STRUAI_API_KEY=... STRUAI_BASE_URL=https://api.stru.ai \
STRUAI_PDF=/absolute/path/to/structural.pdf STRUAI_PAGE=12 \
node scripts/drawings_quickstart.mjs

# Full projects + docquery workflow
STRUAI_API_KEY=... STRUAI_BASE_URL=https://api.stru.ai \
STRUAI_PDF=/absolute/path/to/structural.pdf STRUAI_PAGE=12 \
node scripts/projects_workflow.mjs

# Optional cleanup after full workflow
STRUAI_CLEANUP=1 node scripts/projects_workflow.mjs

# Review workflow (start + refresh)
STRUAI_API_KEY=... STRUAI_BASE_URL=https://api.stru.ai \
STRUAI_REVIEW_FILE_HASH=your_file_hash STRUAI_REVIEW_PAGES=13 \
node scripts/reviews_workflow.mjs

# Review workflow (wait for terminal status)
STRUAI_API_KEY=... STRUAI_BASE_URL=https://api.stru.ai \
STRUAI_REVIEW_FILE_HASH=your_file_hash STRUAI_REVIEW_PAGES=13 STRUAI_REVIEW_WAIT=1 \
node scripts/reviews_workflow.mjs

# Review workflow (custom scout + custom specialist team)
STRUAI_API_KEY=... STRUAI_BASE_URL=https://api.stru.ai \
STRUAI_REVIEW_FILE_HASH=your_file_hash STRUAI_REVIEW_PAGES=13 \
STRUAI_REVIEW_SCOUT_FILE=/absolute/path/to/examples/prompts/page13_review/scout.md \
STRUAI_REVIEW_SPECIALISTS_COMMON_FILE=/absolute/path/to/examples/prompts/page13_review/specialists_common.md \
STRUAI_REVIEW_SPECIALISTS_FILE=/absolute/path/to/examples/prompts/page13_review/specialists.json \
node scripts/reviews_workflow.mjs

See scripts/README.md for quick copy/paste commands.

API Reference

Client

  • new StruAI({ apiKey, baseUrl?, timeout? })
  • client.drawings
  • client.projects
  • client.reviews

Drawings (client.drawings)

  • analyze(file, { page, fileHash? }) -> Promise<DrawingResult>
    • Pass either file or fileHash.
    • file can be a file path string, Blob, ArrayBuffer, or typed array.
  • checkCache(fileHash) -> Promise<DrawingCacheStatus>
  • computeFileHash(file) -> Promise<string>

Projects Top-Level (client.projects)

  • create({ name, description? }) -> Promise<ProjectInstance>
  • list() -> Promise<Project[]>
  • open(projectId, { name?, description? }?) -> ProjectInstance
  • delete(projectId) -> Promise<ProjectDeleteResult>

Reviews Top-Level (client.reviews)

  • create({ file?, pages, fileHash?, projectIds?, scout?, specialistsCommon?, specialists?, customInstructions? }) -> Promise<ReviewInstance>
    • Pass exactly one of file or fileHash.
    • Throws if both are missing or both are provided.
    • specialists must be a non-empty array of {name, instructions} objects when provided.
    • When specialists is provided, scout is also required and must reference each specialist by exact name.
    • Omit scout, specialistsCommon, and specialists to use the default review team.
  • list({ status? }?) -> Promise<Review[]>
  • get(reviewId) -> Promise<ReviewInstance>
  • open(reviewId) -> ReviewInstance

Project Instance (project)

Properties:

  • id, name, description, data
  • sheets, docquery

Methods:

  • delete() -> Promise<ProjectDeleteResult>

Review Instance (review)

Properties:

  • id, data

Methods:

  • refresh() -> Promise<Review>
  • status() -> Promise<Review>
  • wait({ timeoutMs?, pollIntervalMs? }?) -> Promise<Review>
    • Rejects if the review reaches failed.
    • Rejects if the timeout elapses first.
  • questions() -> Promise<ReviewQuestion[]>
  • issues() -> Promise<ReviewIssue[]>

Sheets (project.sheets)

  • add(file, { page, fileHash?, sourceDescription?, onSheetExists?, communityUpdateMode?, semanticIndexUpdateMode? }) -> Promise<Job | JobBatch>
    • page supports 12, '1,3,5-7', 'all'
    • Pass either file or fileHash
  • delete(sheetId) -> Promise<SheetDeleteResult>
  • job(jobId, { page? }?) -> Job

DocQuery (project.docquery)

  • nodeGet(uuid) -> Promise<DocQueryNodeGetResult>
  • sheetEntities(sheetId, { entityType?, limit? }?) -> Promise<DocQuerySheetEntitiesResult>
  • search(query, { index?, limit? }?) -> Promise<DocQuerySearchResult>
  • neighbors(uuid, { mode?, direction?, relationshipType?, radius?, limit? }?) -> Promise<DocQueryNeighborsResult>
  • cypher(query, { params?, maxRows? }?) -> Promise<DocQueryCypherResult>
  • sheetSummary(sheetId, { orphanLimit? }?) -> Promise<DocQuerySheetSummaryResult>
  • sheetList() -> Promise<DocQuerySheetListResult>
  • referenceResolve(uuid, { limit? }?) -> Promise<DocQueryReferenceResolveResult>
  • crop({ output, uuid?, bbox?, pageHash? }) -> Promise<DocQueryCropResult>

CLI parity: project-list maps to client.projects.list(), and the remaining 9 commands map to project.docquery.*, for full 10-command parity.

const project = client.projects.open('proj_86c0f02e');
const cypher = await project.docquery.cypher(
  'MATCH (n:Entity {project_id:$project_id}) RETURN count(n) AS total',
  { params: {}, maxRows: 1 }
);
const crop = await project.docquery.crop({
  uuid: 'entity-uuid-here',
  output: '/absolute/path/to/crop.png',
});
console.log(cypher.records[0]?.total, crop.output_path, crop.bytes_written);

Jobs

Job:

  • id, page
  • status() -> Promise<JobStatus>
  • wait({ timeoutMs?, pollIntervalMs? }?) -> Promise<SheetResult>

JobBatch:

  • jobs, ids
  • statusAll() -> Promise<JobStatus[]>
  • waitAll({ timeoutMs?, pollIntervalMs? }?) -> Promise<SheetResult[]>

Reviews

Review:

  • review_id, status, total_pages, pages, progress
  • is_running, is_complete, is_partial, is_failed, is_terminal
  • Status values: running, completed, completed_partial, failed
  • pages and total_pages are populated by POST /v1/reviews; later refresh() / get() calls reflect the slimmer GET /v1/reviews/{review_id} payload and may omit them.
  • progress.specialist.active contains live in-progress specialist rows with question_id, agent, turns_used, max_turns, and updated_at when the server exposes them.

ReviewQuestion:

  • Includes raw_model_output, preserved as nested JSON when present.

Endpoint Coverage

Full endpoint + response-shape reference: ../docs/HTTP_ENDPOINTS.md

Tier 1:

  • POST /v1/drawings
  • GET /v1/drawings/cache/{file_hash}

Tier 2:

  • POST /v1/projects
  • GET /v1/projects
  • DELETE /v1/projects/{project_id}
  • POST /v1/projects/{project_id}/sheets
  • DELETE /v1/projects/{project_id}/sheets/{sheet_id}
  • GET /v1/projects/{project_id}/jobs/{job_id}
  • GET /v1/projects/{project_id}/node-get
  • GET /v1/projects/{project_id}/sheet-entities
  • GET /v1/projects/{project_id}/search
  • GET /v1/projects/{project_id}/neighbors
  • POST /v1/projects/{project_id}/cypher
  • POST /v1/projects/{project_id}/crop

Tier 3:

  • POST /v1/reviews
  • GET /v1/reviews
  • GET /v1/reviews/{review_id}
  • GET /v1/reviews/{review_id}/questions
  • GET /v1/reviews/{review_id}/issues

License

MIT