Skip to content

Releases: makegov/tango-node

v1.0.0

13 May 15:41
v1.0.0
f3f69ae

Choose a tag to compare

First stable release. tango-node is now at full feature parity with both the Tango API and tango-python for the surface that remains after the subject-based webhook removal (see "Removed" below). Every read method and every endpoint/alert/signing helper available on tango_python.TangoClient has an idiomatic camelCase counterpart on TangoClient, the SDK's docs are auto-published to docs.makegov.com/sdks/node/ via the composer pipeline (makegov/docs#15 / makegov/docs#16), and from 1.x on we'll only ship breaking changes on a major bump.

Changed (breaking)

  • createWebhookEndpoint({ name, ... })name is now required. Previously the SDK silently fell back to the callback URL's host when name was omitted, which masked the server's unique(user, name) constraint until the second duplicate endpoint. Raising client-side gives a clearer error and matches tango-python 1.0.0's behavior.
  • createWebhookAlert({ filters, ... })filters is now validated as a non-empty plain object. Previously {} and arrays passed the check; the error message claimed "non-empty object" but didn't enforce it. Matches the server-side validation.

Fixed

  • tests/unit/client.iterate.test.ts — corrected the comment above the first page assertion; previous text claimed the first call should NOT carry a page, but the assertion (correctly) expects page=1 since listContracts defaults to it.
  • docs/DEVELOPERS.mdlistContracts({ offset: 25 }) example replaced with { page: 2 } and "manual offset tracking" → "manual page/cursor tracking" (the method has never accepted offset).
  • CHANGELOG.md — corrected "4 new unit test files" to "5"; the parenthesized list always contained 5 paths.
  • Lint cleanup ahead of the npm publish — 44 redundant as AnyRecord / as <T> type-assertion casts dropped across src/ (@typescript-eslint/no-unnecessary-type-assertion fires under the newer plugin minor that CI resolves). src/webhooks/receiver.ts: dropped the unused AddressInfo import, simplified Delivery.bodyJson: unknown | nullunknown (the latter already includes null), and restructured WebhookReceiver.run() to avoid const receiver = this (replaced with arrow-function closures over getUrl/getDeliveries/stop). No behavior change — tests still pass 220/220.
  • eslint.config.js — disabled the core no-undef rule for TS files. TypeScript itself does undefined-symbol checking with knowledge of TS-only types (AsyncDisposable, Disposable, etc.); the core rule double-fires and false-flags those. Matches the typescript-eslint upstream guidance.

Internal

  • Pinned devDependencies to exact versions (dropped ^ from package.json). The previous unpinned ranges + gitignored package-lock.json meant CI re-resolved deps on every run and could pick up minor versions with stricter behavior that local installs hadn't seen yet — that's how the lint mismatch above slipped past local checks.

Docs

  • README updated for the docs-review sweep:
    • Added TangoTimeoutError to the documented error class list (it has been exported from src/errors.ts since v0.4 but the README omitted it).
    • Replaced the "(Coming Soon!)" marker on the docs link with the live https://docs.makegov.com/sdks/node/ URL.
    • Rewrote the "Comprehensive API Coverage" feature bullet — the old enumeration listed fewer than half of the actually-implemented domains. New bullet points at the canonical "API Methods" section for the full surface.
  • New docs/CLIENT.mdTangoClient constructor reference, environment variables, full retry/backoff semantics (including Retry-After handling), error-handling patterns, fetchImpl injection, and staging/local targeting. Ported from docs.makegov.com/sdks/node/client.md ahead of the docs-site auto-pull cutover (makegov/docs#15 / makegov/docs#16).
  • docs/API_REFERENCE.md enriched with notes from the docs-site methods.md that hadn't been folded in yet: listContracts page/cursor mutual exclusion, getIdvSummary / listIdvSummaryAwards deprecation (server returns 404), listIdvLcats clarification, listOrganizations level semantics, createWebhookEndpoint snake_case canonical vs camelCase legacy aliases (name is required either way per the 1.0.0 change above), testWebhookEndpoint post-#2252 cleanup ({ endpoint: <id> } is canonical), and createWebhookAlert field-rename notes (name vs subscription_name, filters vs filter_definition, singular query_type, update-writable field list).

CI

  • New .github/workflows/docs-dispatch.yml — fires on push to main when docs/**, README.md, or CHANGELOG.md changes and dispatches external_updated at makegov/docs so the public docs site rebuilds with the latest SDK content. Required for the makegov/docs#15 auto-pull pipeline.

Parity closure — all previously-tracked gaps addressed

Every gap surfaced in the May 2026 parity audit is now closed:

Typed filter Options interfaces

  • listForecasts, listOpportunities, listNotices, listGrants — previously ListOptionsBase & Record<string, unknown> with zero typed filters; now ship full Options interfaces (ListForecastsOptions, ListOpportunitiesOptions, ListNoticesOptions, ListGrantsOptions) enumerating every filter kwarg from the Python signatures. ListNoticesOptions deliberately omits ordering (server rejects it).
  • ListContractsOptions expanded to enumerate all 27+ Python kwargs (award_date*, awarding_agency, funding_agency, obligated_gte/lte, pop_*, expiring_*, keyword/recipient_name/recipient_uei/set_aside_type/naics_code/psc_code aliases, sort+orderordering, etc.).
  • ListEntitiesOptions expanded with the 12 Python kwargs (cage_code, naics, name, psc, purpose_of_registration_code, socioeconomic, state, total_awards_obligated_gte/lte, uei, zip_code).
  • ListVehiclesOptions expanded with all 21 filter kwargs (vehicle_type, type_of_idc, contract_type, who_can_use, total_obligated_min/max, etc.).
  • ListIdvsOptions expanded with the full IDV filter surface.
  • ListOtasOptions / ListOtidvsOptions expanded with the missing _gte/_lte ranges (award_date_gte/lte, fiscal_year_gte/lte, expiring_gte/lte, pop_start_date_gte/lte, pop_end_date_gte/lte).
  • ListAgenciesOptions added (was inline { page?, limit? }).

All Options interfaces keep a [key: string]: unknown index signature for forward-compatibility with new server-side filters that haven't been ported yet — typed fields give autocomplete and typo-protection on known filters; unknown fields still pass through.

ShapeConfig preset parity

Added: PROTESTS_MINIMAL, OTAS_MINIMAL, OTIDVS_MINIMAL, SUBAWARDS_MINIMAL, GSA_ELIBRARY_CONTRACTS_MINIMAL, ORGANIZATIONS_MINIMAL, VEHICLE_ORDERS_MINIMAL, ITDASHBOARD_INVESTMENTS_MINIMAL, ITDASHBOARD_INVESTMENTS_COMPREHENSIVE.

Updated to match Python field lists: ENTITIES_COMPREHENSIVE (now uses federal_obligations(*) expansion), VEHICLES_MINIMAL (extended to mirror Python's larger field list), VEHICLES_COMPREHENSIVE (dropped competition_details(*) per SDK v0.6.0; added program_acronym, is_synthetic_solicitation, metrics(*), organization expansion).

Explicit schemas

Added 11 schemas + registry entries: ORGANIZATION_OFFICE_SCHEMA, VEHICLE_METRICS_SCHEMA, ORGANIZATION_SCHEMA, OTA_SCHEMA, OTIDV_SCHEMA, SUBAWARD_SCHEMA, PROTEST_DOCKET_SCHEMA, PROTEST_SCHEMA, GSA_ELIBRARY_IDV_REF_SCHEMA, GSA_ELIBRARY_CONTRACT_SCHEMA, ITDASHBOARD_INVESTMENT_SCHEMA. Wired into the EXPLICIT_SCHEMAS registry under canonical model names.

Also extended VEHICLE_SCHEMA with the fields the new VEHICLES_* presets reference: is_synthetic_solicitation, program_acronym, organization (expandable to OrganizationOffice), metrics (expandable to VehicleMetrics), idv_count, total_obligated, latest_award_date, opportunity_id, description.

Typed return models

  • client.resolve(input)Promise<ResolveResult> with typed ResolveCandidate[] (was Promise<{ candidates: AnyRecord[]; count: number }>).
  • client.validate(input)Promise<ValidateResult> (was Promise<AnyRecord>).
  • client.getAgency(code)Promise<AgencyRecord> (was Promise<AnyRecord>).
  • client.getProtest(caseNumber)Promise<ProtestRecord> with typed docket, resolved_*, etc.

All new types exported from package root.

Observability — rateLimitInfo + lastResponseHeaders

Two new instance properties on TangoClient mirroring Python's rate_limit_info / last_response_headers:

await client.listContracts({ limit: 5 });
console.log(client.rateLimitInfo);
// { remaining: 98, limit: 100, resetIn: 60, retryAfter: null, limitType: "per_minute" }
console.log(client.lastResponseHeaders?.["x-request-id"]);

Both null until the first request completes; populated after every successful request. HttpClient parses X-RateLimit-{Remaining,Limit,Reset,Type} + Retry-After headers into a RateLimitInfo snapshot.

listContracts cursor pagination (non-breaking)

ListContractsOptions now accepts cursor alongside page. When cursor is supplied, the request omits page and uses keyset pagination (Python parity); otherwise falls back to page-based. PaginatedResponse gained a cursor field auto-extracted from next so callers can client.listContracts({ cursor: resp.cursor }) for the next page without parsing the URL themselves.

WebhookReceiver framework

New src/webhooks/receiver.ts. WebhookReceiver dataclass-style class with start(), stop(), url, deliveries, onDelivery callback, optional forwardTo mirror, history cap, signature verification (via existing verifySignature). Two run patterns shipped:

  • await using rx = await new WebhookReceiver(opts).run(); (modern, Symbol.asyncDispose).
  • `await WebhookReceiver.wit...
Read more

v0.3.0

09 Feb 14:30
914fc83

Choose a tag to compare

Added

  • Vehicles endpoints: listVehicles, getVehicle, and listVehicleAwardees (supports shaping + flattening)
  • IDV endpoints: listIdvs, getIdv, listIdvAwards, listIdvChildIdvs, listIdvTransactions, getIdvSummary, listIdvSummaryAwards
  • Webhooks v2 client support: event type discovery, subscription CRUD, endpoint management, test delivery, sample payload helpers

Changed

  • HTTP client now supports PATCH/PUT/DELETE for non-GET endpoints
  • joiner is now respected when unflattening flat=true responses on supported endpoints

v0.1.4 (2025-11-21)

22 Nov 12:34

Choose a tag to compare

This is the Node/TypeScript port of the official Tango Python SDK.