attio-ts-sdk 0.0.0 → 1.1.0

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/README.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Attio CRM TypeScript SDK
2
2
 
3
+ [![npm version](https://badge.fury.io/js/attio-ts-sdk.svg)](https://www.npmjs.com/package/attio-ts-sdk)
4
+ [![ci](https://github.com/hbmartin/attio-ts-sdk/actions/workflows/ci.yml/badge.svg)](https://github.com/hbmartin/attio-ts-sdk/actions/workflows/ci.yml)
5
+ [![codecov](https://codecov.io/gh/hbmartin/attio-ts-sdk/graph/badge.svg?token=Po1nDYEr5f)](https://codecov.io/gh/hbmartin/attio-ts-sdk)
6
+ [![NPM License](https://img.shields.io/npm/l/attio-ts-sdk?color=blue)](https://github.com/hbmartin/attio-ts-sdk/blob/main/LICENSE)
7
+ [![Context7](https://img.shields.io/badge/[]-Context7-059669)](https://context7.com/hbmartin/attio-ts-sdk)
8
+ [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/hbmartin/attio-ts-sdk)
9
+
3
10
  A modern, type-safe TypeScript SDK for the [Attio](https://attio.com) CRM API. Built with Zod v4 and a new Attio‑aware client layer that adds retries, error normalization, caching, and higher‑level helpers on top of the generated OpenAPI client.
4
11
 
5
12
  - **Create an Attio client in one line** (`createAttioClient({ apiKey })`)
@@ -8,6 +15,8 @@ A modern, type-safe TypeScript SDK for the [Attio](https://attio.com) CRM API. B
8
15
  - **Record normalization** (handles inconsistent response shapes)
9
16
  - **Metadata caching** (attributes, select options, statuses)
10
17
  - **Pagination helpers** (`paginate` + cursor handling)
18
+ - **Response helpers** (`assertOk`, `toResult`)
19
+ - **Offset pagination support** (`paginateOffset`)
11
20
 
12
21
  You still have full access to the generated, spec‑accurate endpoints.
13
22
 
@@ -15,9 +24,7 @@ You still have full access to the generated, spec‑accurate endpoints.
15
24
 
16
25
  - **Full Attio API Coverage** - People, companies, lists, notes, tasks, meetings, webhooks, and more
17
26
  - **Runtime Validation** - Every request and response validated with Zod v4 schemas
18
- - **Tiny Bundle** - Browser build under 3.5KB gzipped
19
27
  - **Tree-Shakeable** - Import only what you need
20
- - **Isomorphic** - Works in Node.js, Bun, Deno, and browsers
21
28
  - **TypeScript First** - Complete type definitions generated from OpenAPI spec
22
29
  - **Attio-Aware Client** - Retries, normalized errors, caching, helpers
23
30
  - **Zero Config** - Sensible defaults, just add your API key
@@ -72,6 +79,42 @@ const { data: people } = await postV2ObjectsByObjectRecordsQuery({
72
79
  });
73
80
  ```
74
81
 
82
+ ### Recommended Pattern
83
+
84
+ Prefer the Attio convenience layer, throw on errors by default, and unwrap responses with helpers.
85
+ This keeps request code compact and consistent.
86
+
87
+ ```typescript
88
+ import {
89
+ assertOk,
90
+ createAttioClient,
91
+ createAttioSdk,
92
+ getV2Objects,
93
+ value,
94
+ } from 'attio-ts-sdk';
95
+
96
+ const client = createAttioClient({
97
+ apiKey: process.env.ATTIO_API_KEY,
98
+ responseStyle: 'data',
99
+ throwOnError: true,
100
+ });
101
+
102
+ const sdk = createAttioSdk({ client });
103
+
104
+ const company = await sdk.records.create({
105
+ object: 'companies',
106
+ values: {
107
+ name: value.string('Acme Corp'),
108
+ domains: value.domain('acme.com'),
109
+ annual_revenue: value.currency(50000, 'USD'),
110
+ },
111
+ });
112
+
113
+ // Use assertOk with generated endpoints when you need raw access
114
+ const objects = assertOk(await getV2Objects({ client }));
115
+ console.log(objects);
116
+ ```
117
+
75
118
  ### Attio Convenience Layer
76
119
 
77
120
  The Attio helpers wrap the generated endpoints with retries, error normalization,
@@ -100,6 +143,36 @@ const matches = await searchRecords({
100
143
  });
101
144
  ```
102
145
 
146
+ ### Schema Helpers
147
+
148
+ Create a schema from cached metadata and use accessors to reduce raw string keys:
149
+
150
+ ```typescript
151
+ import { createSchema } from 'attio-ts-sdk';
152
+
153
+ const schema = await createSchema({
154
+ client,
155
+ target: 'objects',
156
+ identifier: 'companies',
157
+ });
158
+
159
+ const name = schema.getAccessorOrThrow('name').getFirstValue(company);
160
+ ```
161
+
162
+ ### Record Value Helpers
163
+
164
+ ```typescript
165
+ import { getFirstValue, getValue, value } from 'attio-ts-sdk';
166
+
167
+ const values = {
168
+ name: value.string('Acme'),
169
+ domains: value.domain('acme.com'),
170
+ };
171
+
172
+ const name = getFirstValue(company, 'name');
173
+ const domains = getValue(company, 'domains');
174
+ ```
175
+
103
176
  ### Client Configuration
104
177
 
105
178
  ```typescript
@@ -135,10 +208,31 @@ try {
135
208
  }
136
209
  ```
137
210
 
211
+ If you use the generated endpoints directly, you can normalize and unwrap responses:
212
+
213
+ ```typescript
214
+ import { assertOk, toResult, getV2Objects } from 'attio-ts-sdk';
215
+
216
+ const objects = assertOk(await getV2Objects({ client }));
217
+
218
+ const result = toResult(await getV2Objects({ client }));
219
+ if (result.ok) {
220
+ console.log(result.value);
221
+ } else {
222
+ console.error(result.error);
223
+ }
224
+ ```
225
+
138
226
  ### Pagination Helpers
139
227
 
140
228
  ```typescript
141
- import { createAttioClient, paginate, getV2Meetings } from 'attio-ts-sdk';
229
+ import {
230
+ createAttioClient,
231
+ paginate,
232
+ paginateOffset,
233
+ getV2Meetings,
234
+ postV2ObjectsByObjectRecordsQuery,
235
+ } from 'attio-ts-sdk';
142
236
 
143
237
  const client = createAttioClient({ apiKey: process.env.ATTIO_API_KEY });
144
238
 
@@ -150,8 +244,123 @@ const meetings = await paginate(async (cursor) => {
150
244
  });
151
245
  return result;
152
246
  });
247
+
248
+ const offsetResults = await paginateOffset(async (offset, limit) => {
249
+ const result = await postV2ObjectsByObjectRecordsQuery({
250
+ client,
251
+ path: { object: 'companies' },
252
+ body: { offset, limit },
253
+ });
254
+ return result;
255
+ });
256
+ ```
257
+
258
+ ### Caching
259
+
260
+ The SDK includes two levels of caching to reduce API calls and improve performance:
261
+
262
+ #### Metadata Caching
263
+
264
+ Attribute metadata (attributes, select options, and statuses) is automatically cached with a 5-minute TTL. This reduces redundant API calls when working with the same objects repeatedly.
265
+
266
+ ```typescript
267
+ import { getAttributeOptions, getAttributeStatuses, listAttributes } from 'attio-ts-sdk';
268
+
269
+ // These calls are cached for 5 minutes
270
+ const options = await getAttributeOptions({
271
+ client,
272
+ target: 'objects',
273
+ identifier: 'companies',
274
+ attribute: 'stage',
275
+ });
276
+
277
+ // Subsequent calls with the same parameters return cached data
278
+ const optionsAgain = await getAttributeOptions({
279
+ client,
280
+ target: 'objects',
281
+ identifier: 'companies',
282
+ attribute: 'stage',
283
+ }); // Returns cached result, no API call
284
+ ```
285
+
286
+ The metadata caches have the following defaults:
287
+ - **Attributes cache**: 200 entries max
288
+ - **Options cache**: 500 entries max
289
+ - **Statuses cache**: 500 entries max
290
+
291
+ When a cache reaches its limit, the oldest entry is evicted.
292
+
293
+ You can customize TTL, max entries, and adapters per client:
294
+
295
+ ```typescript
296
+ const client = createAttioClient({
297
+ apiKey: process.env.ATTIO_API_KEY,
298
+ cache: {
299
+ enabled: true,
300
+ metadata: {
301
+ ttlMs: 2 * 60 * 1000,
302
+ maxEntries: { attributes: 300, options: 800, statuses: 800 },
303
+ adapter: {
304
+ create: ({ scope, ttlMs, maxEntries }) =>
305
+ new YourCacheAdapter({ scope, ttlMs, maxEntries }),
306
+ },
307
+ },
308
+ },
309
+ });
310
+
311
+ // Clear metadata caches for this client
312
+ client.cache.clear();
313
+ ```
314
+
315
+ #### Client Instance Caching
316
+
317
+ You can cache `AttioClient` instances to reuse them across your application. This is useful when you want to avoid creating new client instances for repeated operations.
318
+
319
+ ```typescript
320
+ import { getAttioClient } from 'attio-ts-sdk';
321
+
322
+ // With cache.key set, the client instance is cached and reused
323
+ const client = getAttioClient({
324
+ apiKey: process.env.ATTIO_API_KEY,
325
+ cache: { key: 'my-app' },
326
+ });
327
+
328
+ // Returns the same cached client instance
329
+ const sameClient = getAttioClient({
330
+ apiKey: process.env.ATTIO_API_KEY,
331
+ cache: { key: 'my-app' },
332
+ });
333
+
334
+ // Disable caching if needed
335
+ const freshClient = getAttioClient({
336
+ apiKey: process.env.ATTIO_API_KEY,
337
+ cache: { enabled: false },
338
+ });
153
339
  ```
154
340
 
341
+ ### Debug Hooks
342
+
343
+ You can tap into request/response/error lifecycles for logging and tracing.
344
+
345
+ ```typescript
346
+ const client = createAttioClient({
347
+ apiKey: process.env.ATTIO_API_KEY,
348
+ hooks: {
349
+ onRequest: ({ request }) => console.log("request", request.method, request.url),
350
+ onResponse: ({ response }) => console.log("response", response.status),
351
+ onError: ({ error }) => console.error("error", error.message),
352
+ },
353
+ });
354
+
355
+ // Or wire a logger (debug/info/warn/error)
356
+ const clientWithLogger = createAttioClient({
357
+ apiKey: process.env.ATTIO_API_KEY,
358
+ logger: console,
359
+ });
360
+ ```
361
+
362
+ Note: `createAttioClient` always creates a new client instance. Use `getAttioClient` when you want caching behavior.
363
+
155
364
  ### Metadata Helpers
156
365
 
157
366
  ```typescript
@@ -329,23 +538,6 @@ const { data: webhook } = await postV2Webhooks({
329
538
  const { data: webhooks } = await getV2Webhooks({ client });
330
539
  ```
331
540
 
332
- ### Browser Usage
333
-
334
- For browsers, import from the `/browser` entry point:
335
-
336
- ```typescript
337
- import { createClient, getV2Self } from 'attio-ts-sdk/browser';
338
-
339
- const client = createClient({
340
- baseUrl: 'https://api.attio.com',
341
- headers: {
342
- Authorization: `Bearer ${apiKey}`,
343
- },
344
- });
345
-
346
- const { data: self } = await getV2Self({ client });
347
- ```
348
-
349
541
  ### Error Handling
350
542
 
351
543
  ```typescript
@@ -380,10 +572,10 @@ try {
380
572
 
381
573
  ### Tools
382
574
 
575
+ - **[Hey API](https://heyapi.dev/)**: OpenAPI client and Zod schema generation
383
576
  - **Biome**: lint and format with a single tool
384
577
  - **Vitest**: fast tests with coverage and thresholds
385
- - **Size Limit**: keep bundles tiny, with CI checks
386
- - **tsdown**: ESM builds for Node and a separate browser bundle
578
+ - **tsdown**: ESM builds for Node
387
579
  - **CI**: lint, typecheck, test, coverage, and size comments/badges
388
580
  - **Deno-friendly**: `.ts` source imports for direct consumption
389
581
  - **OIDC + Provenance**: publish to npm and JSR via manual CI release