@makegov/tango-node 0.1.4
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/.editorconfig +13 -0
- package/.eslintrc.cjs +20 -0
- package/.prettierrc +8 -0
- package/CHANGELOG.md +15 -0
- package/LICENSE +21 -0
- package/README.md +331 -0
- package/dist/client.d.ts +48 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +321 -0
- package/dist/client.js.map +1 -0
- package/dist/config.d.ts +11 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +21 -0
- package/dist/config.js.map +1 -0
- package/dist/errors.d.ts +39 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +78 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/models/Agency.d.ts +8 -0
- package/dist/models/Agency.d.ts.map +1 -0
- package/dist/models/Agency.js +2 -0
- package/dist/models/Agency.js.map +1 -0
- package/dist/models/Contract.d.ts +17 -0
- package/dist/models/Contract.d.ts.map +1 -0
- package/dist/models/Contract.js +2 -0
- package/dist/models/Contract.js.map +1 -0
- package/dist/models/Department.d.ts +5 -0
- package/dist/models/Department.d.ts.map +1 -0
- package/dist/models/Department.js +2 -0
- package/dist/models/Department.js.map +1 -0
- package/dist/models/Entity.d.ts +11 -0
- package/dist/models/Entity.d.ts.map +1 -0
- package/dist/models/Entity.js +2 -0
- package/dist/models/Entity.js.map +1 -0
- package/dist/models/Forecast.d.ts +12 -0
- package/dist/models/Forecast.d.ts.map +1 -0
- package/dist/models/Forecast.js +2 -0
- package/dist/models/Forecast.js.map +1 -0
- package/dist/models/Grant.d.ts +10 -0
- package/dist/models/Grant.d.ts.map +1 -0
- package/dist/models/Grant.js +2 -0
- package/dist/models/Grant.js.map +1 -0
- package/dist/models/Location.d.ts +17 -0
- package/dist/models/Location.d.ts.map +1 -0
- package/dist/models/Location.js +2 -0
- package/dist/models/Location.js.map +1 -0
- package/dist/models/Notice.d.ts +9 -0
- package/dist/models/Notice.d.ts.map +1 -0
- package/dist/models/Notice.js +2 -0
- package/dist/models/Notice.js.map +1 -0
- package/dist/models/Opportunity.d.ts +11 -0
- package/dist/models/Opportunity.d.ts.map +1 -0
- package/dist/models/Opportunity.js +2 -0
- package/dist/models/Opportunity.js.map +1 -0
- package/dist/models/RecipientProfile.d.ts +12 -0
- package/dist/models/RecipientProfile.d.ts.map +1 -0
- package/dist/models/RecipientProfile.js +2 -0
- package/dist/models/RecipientProfile.js.map +1 -0
- package/dist/models/index.d.ts +11 -0
- package/dist/models/index.d.ts.map +1 -0
- package/dist/models/index.js +2 -0
- package/dist/models/index.js.map +1 -0
- package/dist/shapes/explicitSchemas.d.ts +24 -0
- package/dist/shapes/explicitSchemas.d.ts.map +1 -0
- package/dist/shapes/explicitSchemas.js +1818 -0
- package/dist/shapes/explicitSchemas.js.map +1 -0
- package/dist/shapes/factory.d.ts +29 -0
- package/dist/shapes/factory.d.ts.map +1 -0
- package/dist/shapes/factory.js +93 -0
- package/dist/shapes/factory.js.map +1 -0
- package/dist/shapes/generator.d.ts +53 -0
- package/dist/shapes/generator.d.ts.map +1 -0
- package/dist/shapes/generator.js +117 -0
- package/dist/shapes/generator.js.map +1 -0
- package/dist/shapes/index.d.ts +8 -0
- package/dist/shapes/index.d.ts.map +1 -0
- package/dist/shapes/index.js +8 -0
- package/dist/shapes/index.js.map +1 -0
- package/dist/shapes/parser.d.ts +36 -0
- package/dist/shapes/parser.d.ts.map +1 -0
- package/dist/shapes/parser.js +169 -0
- package/dist/shapes/parser.js.map +1 -0
- package/dist/shapes/schema.d.ts +30 -0
- package/dist/shapes/schema.d.ts.map +1 -0
- package/dist/shapes/schema.js +63 -0
- package/dist/shapes/schema.js.map +1 -0
- package/dist/shapes/schemaTypes.d.ts +42 -0
- package/dist/shapes/schemaTypes.d.ts.map +1 -0
- package/dist/shapes/schemaTypes.js +2 -0
- package/dist/shapes/schemaTypes.js.map +1 -0
- package/dist/shapes/types.d.ts +46 -0
- package/dist/shapes/types.d.ts.map +1 -0
- package/dist/shapes/types.js +25 -0
- package/dist/shapes/types.js.map +1 -0
- package/dist/types.d.ts +28 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/dates.d.ts +14 -0
- package/dist/utils/dates.d.ts.map +1 -0
- package/dist/utils/dates.js +25 -0
- package/dist/utils/dates.js.map +1 -0
- package/dist/utils/http.d.ts +23 -0
- package/dist/utils/http.d.ts.map +1 -0
- package/dist/utils/http.js +158 -0
- package/dist/utils/http.js.map +1 -0
- package/dist/utils/number.d.ts +14 -0
- package/dist/utils/number.d.ts.map +1 -0
- package/dist/utils/number.js +14 -0
- package/dist/utils/number.js.map +1 -0
- package/dist/utils/unflatten.d.ts +11 -0
- package/dist/utils/unflatten.d.ts.map +1 -0
- package/dist/utils/unflatten.js +49 -0
- package/dist/utils/unflatten.js.map +1 -0
- package/docs/API_REFERENCE.md +201 -0
- package/docs/DYNAMIC_MODELS.md +127 -0
- package/docs/SHAPES.md +94 -0
- package/eslint.config.js +37 -0
- package/package.json +73 -0
- package/tsconfig.json +21 -0
- package/vitest.config.ts +18 -0
package/.editorconfig
ADDED
package/.eslintrc.cjs
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/* eslint-env node */
|
|
2
|
+
module.exports = {
|
|
3
|
+
root: true,
|
|
4
|
+
parser: "@typescript-eslint/parser",
|
|
5
|
+
parserOptions: {
|
|
6
|
+
ecmaVersion: "latest",
|
|
7
|
+
sourceType: "module",
|
|
8
|
+
},
|
|
9
|
+
env: {
|
|
10
|
+
node: true,
|
|
11
|
+
es2022: true,
|
|
12
|
+
},
|
|
13
|
+
plugins: ["@typescript-eslint"],
|
|
14
|
+
extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
|
|
15
|
+
ignorePatterns: ["dist/", "node_modules/"],
|
|
16
|
+
rules: {
|
|
17
|
+
"@typescript-eslint/explicit-module-boundary-types": "off",
|
|
18
|
+
"@typescript-eslint/no-explicit-any": "off",
|
|
19
|
+
},
|
|
20
|
+
};
|
package/.prettierrc
ADDED
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `@makegov/tango-node` will be documented in this file.
|
|
4
|
+
|
|
5
|
+
This project follows [Semantic Versioning](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
## [0.1.0] - 2025-11-21
|
|
8
|
+
|
|
9
|
+
- Initial Node.js port of the Tango Python SDK.
|
|
10
|
+
- Basic project scaffolding for client, models, and shapes.
|
|
11
|
+
- ESM + TypeScript build configuration.
|
|
12
|
+
|
|
13
|
+
## [0.1.4] - 2025-11-21
|
|
14
|
+
|
|
15
|
+
- Added tests and cleaned up formatting and structure of SDK.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 MakeGov
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
# Tango Node SDK
|
|
2
|
+
|
|
3
|
+
A modern Node.js SDK for the [Tango API](https://tango.makegov.com), featuring dynamic response shaping, strong TypeScript types, and full coverage of the core Tango endpoints.
|
|
4
|
+
|
|
5
|
+
> This is the Node/TypeScript port of the official Tango Python SDK.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Dynamic Response Shaping** – Ask Tango for exactly the fields you want using a simple shape syntax.
|
|
10
|
+
- **Type-Safe by Design** – Shape strings are validated against Tango schemas and mapped to generated TypeScript types.
|
|
11
|
+
- **Comprehensive API Coverage** – Agencies, business types, entities, contracts, forecasts, opportunities, notices, and grants.
|
|
12
|
+
- **Flexible Data Access** – Plain JavaScript objects backed by runtime validation and parsing, materialized via the dynamic model pipeline.
|
|
13
|
+
- **Modern Node** – Built for Node 18+ with native `fetch` and ESM-first design.
|
|
14
|
+
- **Tested Against the Real API** – Integration tests (mirroring the Python SDK) keep behavior aligned.
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
**Requirements:** Node 18 or higher.
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install @makegov/tango-node
|
|
22
|
+
# or
|
|
23
|
+
yarn add @makegov/tango-node
|
|
24
|
+
# or
|
|
25
|
+
pnpm add @makegov/tango-node
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Quick Start
|
|
29
|
+
|
|
30
|
+
### Initialize the client
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
import { TangoClient } from "@makegov/tango-node";
|
|
34
|
+
|
|
35
|
+
const client = new TangoClient({
|
|
36
|
+
apiKey: process.env.TANGO_API_KEY,
|
|
37
|
+
// baseUrl: "https://tango.makegov.com", // default
|
|
38
|
+
});
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### List agencies
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
const agencies = await client.listAgencies();
|
|
45
|
+
|
|
46
|
+
for (const agency of agencies.results) {
|
|
47
|
+
console.log(agency.code, agency.name);
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Get a specific agency
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
const treasury = await client.getAgency("2000"); // Treasury
|
|
55
|
+
console.log(treasury.name, treasury.department?.name);
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Search contracts with a minimal shape
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
import { TangoClient, ShapeConfig } from "@makegov/tango-node";
|
|
62
|
+
|
|
63
|
+
const client = new TangoClient({ apiKey: process.env.TANGO_API_KEY });
|
|
64
|
+
|
|
65
|
+
const contracts = await client.listContracts({
|
|
66
|
+
shape: ShapeConfig.CONTRACTS_MINIMAL,
|
|
67
|
+
keyword: "cloud services",
|
|
68
|
+
awarding_agency: "4700",
|
|
69
|
+
fiscal_year: 2024,
|
|
70
|
+
limit: 10,
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// Each contract is shaped according to CONTRACTS_MINIMAL
|
|
74
|
+
for (const c of contracts.results) {
|
|
75
|
+
console.log(c.piid, c.award_date, c.recipient.display_name);
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Get a fully-shaped entity
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
import { TangoClient, ShapeConfig } from "@makegov/tango-node";
|
|
83
|
+
|
|
84
|
+
const client = new TangoClient({ apiKey: process.env.TANGO_API_KEY });
|
|
85
|
+
|
|
86
|
+
const entity = await client.getEntity("ABC123DEF456", {
|
|
87
|
+
shape: ShapeConfig.ENTITIES_COMPREHENSIVE,
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
console.log(entity.uei, entity.legal_business_name, entity.primary_naics);
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Authentication
|
|
94
|
+
|
|
95
|
+
The Node SDK uses the same model as the Python one: you can either pass the API key directly or read it from `TANGO_API_KEY`.
|
|
96
|
+
|
|
97
|
+
### With API key
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
import { TangoClient } from "@makegov/tango-node";
|
|
101
|
+
|
|
102
|
+
const client = new TangoClient({
|
|
103
|
+
apiKey: "your-api-key-here",
|
|
104
|
+
});
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### From environment variable (`TANGO_API_KEY`)
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
import { TangoClient } from "@makegov/tango-node";
|
|
111
|
+
|
|
112
|
+
const client = new TangoClient();
|
|
113
|
+
// If apiKey is omitted, the client will look for process.env.TANGO_API_KEY
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Core Concepts
|
|
117
|
+
|
|
118
|
+
### Dynamic Response Shaping
|
|
119
|
+
|
|
120
|
+
Response shaping is the core feature of Tango. Instead of always receiving huge objects with every field, you describe the fields you want with a compact shape string:
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
const contracts = await client.listContracts({
|
|
124
|
+
shape: "key,piid,award_date,recipient(display_name),total_contract_value",
|
|
125
|
+
keyword: "software",
|
|
126
|
+
limit: 5,
|
|
127
|
+
});
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
**Shapes:**
|
|
131
|
+
|
|
132
|
+
- Reduce payload size (often massively).
|
|
133
|
+
- Keep responses focused on what your app actually uses.
|
|
134
|
+
- Drive type safety – the SDK maps the shape to a TypeScript type.
|
|
135
|
+
|
|
136
|
+
**The Node SDK includes:**
|
|
137
|
+
|
|
138
|
+
- A **shape parser** that validates shape strings.
|
|
139
|
+
- A **schema registry** that knows what fields exist on each resource.
|
|
140
|
+
- A **type generator** and **model factory** that convert raw API JSON into strongly-typed objects.
|
|
141
|
+
|
|
142
|
+
### Flat vs nested responses
|
|
143
|
+
|
|
144
|
+
By default, nested fields are returned as nested objects:
|
|
145
|
+
|
|
146
|
+
```ts
|
|
147
|
+
// shape:
|
|
148
|
+
"key,piid,recipient(display_name,uei)";
|
|
149
|
+
|
|
150
|
+
//
|
|
151
|
+
contract.recipient.display_name;
|
|
152
|
+
contract.recipient.uei;
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
You can request a "flat" representation that uses dotted keys and then unflattens into nested objects on the client:
|
|
156
|
+
|
|
157
|
+
```ts
|
|
158
|
+
const contracts = await client.listContracts({
|
|
159
|
+
shape: ShapeConfig.CONTRACTS_MINIMAL,
|
|
160
|
+
flat: true,
|
|
161
|
+
});
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
The Node SDK mirrors the Python client's behavior for `shape`, `flat`, and `flat_lists`.
|
|
165
|
+
|
|
166
|
+
## API Methods
|
|
167
|
+
|
|
168
|
+
The Node client mirrors the Python SDK's high-level API:
|
|
169
|
+
|
|
170
|
+
- `listAgencies(options)`
|
|
171
|
+
- `getAgency(code)`
|
|
172
|
+
- `listBusinessTypes(options)`
|
|
173
|
+
- `listContracts(options)`
|
|
174
|
+
- `listEntities(options)`
|
|
175
|
+
- `getEntity(ueiOrCage, options)`
|
|
176
|
+
- `listForecasts(options)`
|
|
177
|
+
- `listOpportunities(options)`
|
|
178
|
+
- `listNotices(options)`
|
|
179
|
+
- `listGrants(options)`
|
|
180
|
+
|
|
181
|
+
All list methods return a paginated response:
|
|
182
|
+
|
|
183
|
+
```ts
|
|
184
|
+
interface PaginatedResponse<T> {
|
|
185
|
+
count: number;
|
|
186
|
+
next: string | null;
|
|
187
|
+
previous: string | null;
|
|
188
|
+
pageMetadata: Record<string, unknown> | null;
|
|
189
|
+
results: T[];
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## Error Handling
|
|
194
|
+
|
|
195
|
+
Errors are surfaced as typed exceptions, aligned with the Python SDK:
|
|
196
|
+
|
|
197
|
+
- `TangoAPIError` – Base error for unexpected issues.
|
|
198
|
+
- `TangoAuthError` – Authentication problems (e.g., invalid API key, 401).
|
|
199
|
+
- `TangoNotFoundError` – Resource not found (404).
|
|
200
|
+
- `TangoValidationError` – Invalid request parameters (400).
|
|
201
|
+
- `TangoRateLimitError` – Rate limit exceeded (429).
|
|
202
|
+
|
|
203
|
+
Shape-related errors:
|
|
204
|
+
|
|
205
|
+
- `ShapeError`
|
|
206
|
+
- `ShapeValidationError`
|
|
207
|
+
- `ShapeParseError`
|
|
208
|
+
- `TypeGenerationError`
|
|
209
|
+
- `ModelInstantiationError`
|
|
210
|
+
|
|
211
|
+
Use them in your code:
|
|
212
|
+
|
|
213
|
+
```ts
|
|
214
|
+
import { TangoClient, TangoAPIError, TangoValidationError } from "@makegov/tango-node";
|
|
215
|
+
|
|
216
|
+
try {
|
|
217
|
+
const resp = await client.listContracts({ keyword: "cloud", limit: 5 });
|
|
218
|
+
} catch (err) {
|
|
219
|
+
if (err instanceof TangoValidationError) {
|
|
220
|
+
console.error("Bad request:", err.message);
|
|
221
|
+
} else if (err instanceof TangoAPIError) {
|
|
222
|
+
console.error("Tango API error:", err.message);
|
|
223
|
+
} else {
|
|
224
|
+
console.error("Unexpected error:", err);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
## Project Structure
|
|
230
|
+
|
|
231
|
+
```text
|
|
232
|
+
tango-node/
|
|
233
|
+
├── src/ # Source TypeScript
|
|
234
|
+
│ ├── client.ts # TangoClient implementation
|
|
235
|
+
│ ├── config.ts # Default base URL + shape presets
|
|
236
|
+
│ ├── errors.ts # Error classes (API, auth, validation, etc.)
|
|
237
|
+
│ ├── index.ts # Public API exports
|
|
238
|
+
│ ├── types.ts # Shared types (options, PaginatedResponse)
|
|
239
|
+
│ ├── models/ # Lightweight model interfaces (Contract, Entity, etc.)
|
|
240
|
+
│ ├── shapes/ # Shape system (parser, generator, factory)
|
|
241
|
+
│ │ ├── explicitSchemas.ts # Predefined schemas for resources
|
|
242
|
+
│ │ ├── factory.ts # Instantiate typed models from data
|
|
243
|
+
│ │ ├── generator.ts # Type generation from shape specs
|
|
244
|
+
│ │ ├── index.ts # Shapes exports
|
|
245
|
+
│ │ ├── parser.ts # Shape string parser
|
|
246
|
+
│ │ ├── schema.ts # Schema registry + validation
|
|
247
|
+
│ │ ├── schemaTypes.ts # Schema data structures
|
|
248
|
+
│ │ └── types.ts # Shape spec types
|
|
249
|
+
│ └── utils/ # Helpers
|
|
250
|
+
│ ├── dates.ts # Date/time parsing utilities
|
|
251
|
+
│ ├── http.ts # HTTP client wrapper
|
|
252
|
+
│ ├── number.ts # Numeric parsing/formatting
|
|
253
|
+
│ └── unflatten.ts # Unflatten dotted-key responses
|
|
254
|
+
├── docs/ # Documentation
|
|
255
|
+
│ ├── API_REFERENCE.md
|
|
256
|
+
│ ├── DYNAMIC_MODELS.md
|
|
257
|
+
│ └── SHAPED.md
|
|
258
|
+
├── tests/ # Test suite (Vitest)
|
|
259
|
+
│ └── unit/
|
|
260
|
+
│ ├── client.test.ts
|
|
261
|
+
│ ├── errors.test.ts
|
|
262
|
+
│ ├── shapes.factory.test.ts
|
|
263
|
+
│ ├── shapes.generator.test.ts
|
|
264
|
+
│ ├── shapes.parser.test.ts
|
|
265
|
+
│ ├── shapes.schema.test.ts
|
|
266
|
+
│ ├── utils.dates.test.ts
|
|
267
|
+
│ ├── utils.http.test.ts
|
|
268
|
+
│ ├── utils.number.test.ts
|
|
269
|
+
│ └── utils.unflatten.test.ts
|
|
270
|
+
├── dist/ # Build output (compiled JS + d.ts) from `npm run build`
|
|
271
|
+
├── eslint.config.js # ESLint flat config
|
|
272
|
+
├── .prettierrc # Prettier config
|
|
273
|
+
├── package.json # Package metadata/scripts
|
|
274
|
+
├── tsconfig.json # TypeScript config
|
|
275
|
+
├── README.md # Usage docs
|
|
276
|
+
├── CHANGELOG.md # Version history
|
|
277
|
+
└── LICENSE # MIT license
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
## Development
|
|
281
|
+
|
|
282
|
+
After cloning the repo:
|
|
283
|
+
|
|
284
|
+
```bash
|
|
285
|
+
npm install
|
|
286
|
+
npm run build
|
|
287
|
+
npm test
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
Useful scripts:
|
|
291
|
+
|
|
292
|
+
- `npm run build` – Compile TypeScript to `dist/`.
|
|
293
|
+
- `npm test` – Run unit and integration tests.
|
|
294
|
+
- `npm run coverage` – Get test coverage report.
|
|
295
|
+
- `npm run lint` – Run ESLint.
|
|
296
|
+
- `npm run format` – Run Prettier.
|
|
297
|
+
- `npm run typecheck` – TS type checking without emit.
|
|
298
|
+
|
|
299
|
+
## Requirements
|
|
300
|
+
|
|
301
|
+
- Node 18 or higher.
|
|
302
|
+
- A valid [Tango API key](https://tango.makegov.com/).
|
|
303
|
+
|
|
304
|
+
## Documentation
|
|
305
|
+
|
|
306
|
+
- [API Reference](docs/API_REFERENCE.md) - Detailed API documentation
|
|
307
|
+
- [Shape System Guide](docs/SHAPES.md) - Comprehensive guide to response shaping
|
|
308
|
+
- [Dynamic Models Guide](docs/DYNAMIC_MODELS.md) - ynamic shaping system\*\* works.
|
|
309
|
+
|
|
310
|
+
## License
|
|
311
|
+
|
|
312
|
+
MIT License - see [LICENSE](LICENSE) for details.
|
|
313
|
+
|
|
314
|
+
## Support
|
|
315
|
+
|
|
316
|
+
For questions, issues, or feature requests:
|
|
317
|
+
|
|
318
|
+
- **Email**: [tango@makegov.com](mailto:tango@makegov.com)
|
|
319
|
+
- **Issues**: [GitHub Issues](https://github.com/makegov/tango-node/issues)
|
|
320
|
+
- **Documentation**: [https://tango.makegov.com/docs/tango-node](https://tango.makegov.com/docs/tango-node) _(Coming Soon!)_
|
|
321
|
+
|
|
322
|
+
## Contributing
|
|
323
|
+
|
|
324
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
325
|
+
|
|
326
|
+
1. Fork the repository
|
|
327
|
+
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
|
328
|
+
3. Run tests (`npm run test`)
|
|
329
|
+
4. Commit your changes (`git commit -m 'Add amazing feature'`)
|
|
330
|
+
5. Push to the branch (`git push origin feature/amazing-feature`)
|
|
331
|
+
6. Open a Pull Request
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { PaginatedResponse, TangoClientOptions } from "./types.js";
|
|
2
|
+
type AnyRecord = Record<string, unknown>;
|
|
3
|
+
export interface ListOptionsBase {
|
|
4
|
+
page?: number;
|
|
5
|
+
limit?: number;
|
|
6
|
+
shape?: string | null;
|
|
7
|
+
flat?: boolean;
|
|
8
|
+
flatLists?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface ListContractsOptions extends ListOptionsBase {
|
|
11
|
+
filters?: AnyRecord;
|
|
12
|
+
[key: string]: unknown;
|
|
13
|
+
}
|
|
14
|
+
export interface ListEntitiesOptions extends ListOptionsBase {
|
|
15
|
+
search?: string;
|
|
16
|
+
[key: string]: unknown;
|
|
17
|
+
}
|
|
18
|
+
export declare class TangoClient {
|
|
19
|
+
private readonly http;
|
|
20
|
+
private readonly shapeParser;
|
|
21
|
+
private readonly modelFactory;
|
|
22
|
+
constructor(options?: TangoClientOptions);
|
|
23
|
+
listAgencies(options?: {
|
|
24
|
+
page?: number;
|
|
25
|
+
limit?: number;
|
|
26
|
+
}): Promise<PaginatedResponse<AnyRecord>>;
|
|
27
|
+
getAgency(code: string): Promise<AnyRecord>;
|
|
28
|
+
listBusinessTypes(options?: {
|
|
29
|
+
page?: number;
|
|
30
|
+
limit?: number;
|
|
31
|
+
}): Promise<PaginatedResponse<AnyRecord>>;
|
|
32
|
+
listContracts(options?: ListContractsOptions): Promise<PaginatedResponse<Record<string, unknown>>>;
|
|
33
|
+
listEntities(options?: ListEntitiesOptions): Promise<PaginatedResponse<Record<string, unknown>>>;
|
|
34
|
+
getEntity(key: string, options?: {
|
|
35
|
+
shape?: string | null;
|
|
36
|
+
flat?: boolean;
|
|
37
|
+
flatLists?: boolean;
|
|
38
|
+
}): Promise<Record<string, unknown>>;
|
|
39
|
+
listForecasts(options?: ListOptionsBase & Record<string, unknown>): Promise<PaginatedResponse<Record<string, unknown>>>;
|
|
40
|
+
listOpportunities(options?: ListOptionsBase & Record<string, unknown>): Promise<PaginatedResponse<Record<string, unknown>>>;
|
|
41
|
+
listNotices(options?: ListOptionsBase & Record<string, unknown>): Promise<PaginatedResponse<Record<string, unknown>>>;
|
|
42
|
+
listGrants(options?: ListOptionsBase & Record<string, unknown>): Promise<PaginatedResponse<Record<string, unknown>>>;
|
|
43
|
+
private parseShape;
|
|
44
|
+
private materializeList;
|
|
45
|
+
private materializeOne;
|
|
46
|
+
}
|
|
47
|
+
export {};
|
|
48
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|