@curviate/sdk 0.24.3 → 0.26.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/CHANGELOG.md +174 -1
- package/README.md +78 -0
- package/dist/index.d.ts +2019 -294
- package/dist/index.js +118 -7
- package/package.json +3 -1
- package/src/generated/types.ts +2037 -462
package/CHANGELOG.md
CHANGED
|
@@ -7,7 +7,180 @@ Versioning: semantic. Minor for additive changes, patch for bug fixes; no stabil
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
-
## [
|
|
10
|
+
## [0.26.0] - 2026-09-06
|
|
11
|
+
|
|
12
|
+
Fixture regenerated against the deployed production document
|
|
13
|
+
(`https://api.curviate.com`, `server_git_sha bd795601...`, 125 paths).
|
|
14
|
+
**BREAKING at the type level** for a consumer that reads `row` off an error
|
|
15
|
+
or a safety warning as a `string`: it is now `string | null` on both. Three
|
|
16
|
+
response enums also widen, which reds an exhaustive `switch` with a `never`
|
|
17
|
+
default. Everything else in this release is additive.
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- **`NOT_STORED` in the error taxonomy.** The `422` a read gets when
|
|
22
|
+
`mode=cache_only` (or `refill` on a resource that cannot be filled) finds
|
|
23
|
+
nothing in Curviate's store, so the API refuses rather than reaching
|
|
24
|
+
LinkedIn. It was missing from `ERROR_CODES`, so it decoded to `INTERNAL`:
|
|
25
|
+
a caller could not branch on it, and, the harmful half, `INTERNAL` is
|
|
26
|
+
retryable, so a `cache_only` miss was retried on a GET against an answer
|
|
27
|
+
that cannot change until the caller picks another mode. It is now decoded to
|
|
28
|
+
itself and is deliberately absent from the retryable set. Not the same as
|
|
29
|
+
`RESOURCE_NOT_FOUND`: the resource may exist and this API simply holds no
|
|
30
|
+
copy, so the fix is another mode, not another id. `cache_only` is the mode
|
|
31
|
+
that raises it on these reads, and `refill` or `auto` is the remedy.
|
|
32
|
+
|
|
33
|
+
**This reds `@curviate/cli`'s exit-code exhaustiveness guard, by design.**
|
|
34
|
+
That guard iterates the exported `ERROR_CODES` array rather than a copy of
|
|
35
|
+
it, which is the whole reason the array is exported, so a code added here
|
|
36
|
+
with no exit-code mapping is supposed to fail it. Map `NOT_STORED` there when
|
|
37
|
+
bumping the pin.
|
|
38
|
+
- **`messaging.getChat(chatId, params?)` takes the retrieval query.**
|
|
39
|
+
`GET /v1/{account_id}/chats/{chat_id}` declares `expand`, `mode` and
|
|
40
|
+
`max_age`; the method sent no query at all, so a chat read was the one
|
|
41
|
+
store-servable read with no retrieval ladder. `ChatGetQuery` is derived from
|
|
42
|
+
the generated `paths` types, like every other query type here.
|
|
43
|
+
`users.get()` and `messaging.listMessages()` already carried the pair and
|
|
44
|
+
are unchanged; the README now documents all three.
|
|
45
|
+
|
|
46
|
+
### Regenerated types
|
|
47
|
+
|
|
48
|
+
- **BREAKING - `row` is nullable on both `Error` and `SafetyWarning`.**
|
|
49
|
+
`Error.row` goes `string | undefined` to `string | null | undefined`, and
|
|
50
|
+
`SafetyWarning.row` - which is REQUIRED, so it is the one that will actually
|
|
51
|
+
break a build - goes `string` to `string | null`. `null` says the breach
|
|
52
|
+
names no row: an `activity_window` refusal on an action that spends no
|
|
53
|
+
budget of its own has an hour to answer for and no counter. Read
|
|
54
|
+
`hint.parameter` in that case; it names `posture`. A consumer that assigned
|
|
55
|
+
`.row` to a `string` or passed it somewhere non-null now has a type error,
|
|
56
|
+
and that error is correct - the runtime value was always going to be `null`
|
|
57
|
+
once the server started sending it. The SDK's own `CurviateError.budgetRow`
|
|
58
|
+
is UNCHANGED (`string | undefined`): `transport.ts` maps the wire field with
|
|
59
|
+
a `typeof === "string"` guard, so a `null` row arrives as an absent
|
|
60
|
+
`budgetRow`, which is what "no row to name" already meant on that surface.
|
|
61
|
+
- **BREAKING for an exhaustive `switch` - three response enums widen.** Each
|
|
62
|
+
gains a member, so a `switch` with a `never`-typed default arm stops
|
|
63
|
+
compiling. None of them removes a member, so a `switch` with a real default
|
|
64
|
+
keeps working:
|
|
65
|
+
- `GET /v1/{account_id}/safety-events` `items[].reason` gains
|
|
66
|
+
`activity_window` alongside `budget_exhausted` and `rate_limited`.
|
|
67
|
+
- `GET /v1/{account_id}/safety-events` `items[].budget_row` gains `"*"`, the
|
|
68
|
+
wildcard for an event that is not scoped to one row.
|
|
69
|
+
- `notices[].code` on the ten chat and message reads that accept
|
|
70
|
+
`expand=public_identifier` gains `EXPANSION_WITHHELD_ACTIVITY_WINDOW`
|
|
71
|
+
alongside `EXPANSION_LIMIT_REACHED`: the expansion was refused because the
|
|
72
|
+
account is outside its activity window, not because the page ran out of
|
|
73
|
+
lookup budget. Same consequence for the reader either way -
|
|
74
|
+
`public_identifier` is `null` for reasons that are not "no public profile"
|
|
75
|
+
- but a different fix, so it is a separate code. The `notices` key itself
|
|
76
|
+
is not new.
|
|
77
|
+
- **Additive - `interface_ceiling` and `effective_interface_ceiling` on every
|
|
78
|
+
budget row.** Optional, `number | null`. They appear on `quotas[]` from
|
|
79
|
+
`GET /v1/accounts/{account_id}` and on the policy rows from
|
|
80
|
+
`GET`/`PATCH /v1/{account_id}/safety-policy`. What a call through the
|
|
81
|
+
matching ELEVATED interface (`/sales-navigator/...`, `/recruiter/...`) is
|
|
82
|
+
held to on that row; `null` wherever the row and limit profile have no such
|
|
83
|
+
figure, which is every row on `basic` and `premium`. `ceiling`,
|
|
84
|
+
`effective_ceiling`, `band` and `over_default` all remain the STANDARD
|
|
85
|
+
figure, so a row can report `band: "over"` while an elevated call still
|
|
86
|
+
succeeds. Read `effective_interface_ceiling`, not `interface_ceiling`, for
|
|
87
|
+
the same reason `effective_ceiling` and not `ceiling` answers that on the
|
|
88
|
+
standard side. On the `PATCH` request body both are typed `unknown` and
|
|
89
|
+
documented READ-ONLY: sent back they are accepted and ignored, so a read can
|
|
90
|
+
round-trip unedited.
|
|
91
|
+
- **Additive - `activity_window` on the `PATCH /v1/{account_id}/safety-policy`
|
|
92
|
+
request body**, typed `unknown` and documented NOT ACCEPTED. Send the
|
|
93
|
+
settable subfields instead: top-level `timezone`, and per row
|
|
94
|
+
`activity_window_start`, `activity_window_end`, `activity_window_timezone`,
|
|
95
|
+
`activity_window_applies_to`.
|
|
96
|
+
- Description-only, no type change: `reset_at` on `Error` and `SafetyWarning`
|
|
97
|
+
now states that it is the window roll on a `ceiling` refusal and the next
|
|
98
|
+
window open on an `activity_window` one, and restates the two cases where no
|
|
99
|
+
clock frees the account (the `pending_invites` gauge, and InMail credits).
|
|
100
|
+
The chat-search `q` parameter documents that a 1-2 character term is slower.
|
|
101
|
+
|
|
102
|
+
## [0.25.0] - 2026-09-05
|
|
103
|
+
|
|
104
|
+
**BREAKING** for a consumer that pinned the old type declarations: `quotas[]`
|
|
105
|
+
on `GET /v1/accounts/{account_id}` is a different array, and `message_id` on
|
|
106
|
+
`startChat`, `sendMessage`, `sendInMail` and the company chat reply is now
|
|
107
|
+
`string | string[] | null` where it was `string`. Both are detailed under
|
|
108
|
+
"Regenerated types" below. Pre-1.0 a breaking change ships as a minor, so a
|
|
109
|
+
caret range on `0.24.x` will not pick this up.
|
|
110
|
+
|
|
111
|
+
### Added
|
|
112
|
+
|
|
113
|
+
- **`BUDGET_EXHAUSTED`, the account-safety refusal, and its payload.** A `429`
|
|
114
|
+
that is Curviate's own ceiling rather than a request-rate limit: nothing
|
|
115
|
+
reached LinkedIn and nothing was spent. `CurviateError` gains `budgetRow`
|
|
116
|
+
(wire `row`), `resetAt` (`reset_at`, and `null` in the two cases no clock
|
|
117
|
+
frees: the `pending_invites` gauge and an InMail credit exhaustion),
|
|
118
|
+
`safetyHint` (`hint`, the settable parameter
|
|
119
|
+
on `PATCH /v1/{account_id}/safety-policy`), `safetyReason` (`reason`:
|
|
120
|
+
`ceiling` or `activity_window`) and `blocked`. All six ride `toJSON()`.
|
|
121
|
+
The code is deliberately NOT retryable: a monthly row's reset can be weeks
|
|
122
|
+
out, so a backoff loop burns retries against a wall that will not move.
|
|
123
|
+
On a successful response the same payload arrives under `safety_warning`
|
|
124
|
+
with `blocked: false`, typed on the generated response types.
|
|
125
|
+
- **`budgetRow` and `retryAfterSeconds` on a paused-row `PLATFORM_RATE_LIMIT`.**
|
|
126
|
+
Same wire `row`, different meaning: LinkedIn refused a recent call on that
|
|
127
|
+
row, so the next one is refused locally until the pause lifts. The pause is
|
|
128
|
+
scoped to `(account, row)`, so the recovery is to switch work rather than
|
|
129
|
+
back off across the account. `retry_after` is deliberately NOT folded into
|
|
130
|
+
`retryAfterMs`, which everything sleeps.
|
|
131
|
+
- **A response carrying `row` is never auto-retried, on any method.**
|
|
132
|
+
- **`LINKEDIN_SESSION_EVICTED`.** A 401 split from `LINKEDIN_AUTH_FAILED`
|
|
133
|
+
because the remedies differ: reconnecting does nothing while the other
|
|
134
|
+
session is open. Not retryable.
|
|
135
|
+
|
|
136
|
+
### Changed
|
|
137
|
+
|
|
138
|
+
- **Fixture regenerated against the deployed production document**
|
|
139
|
+
(`https://api.curviate.com`, `git_sha 33b42521...`), and the generated
|
|
140
|
+
types with it. Two shape changes are BREAKING for a consumer that pinned
|
|
141
|
+
the old declarations, and one enum widened. See "Regenerated types" below.
|
|
142
|
+
|
|
143
|
+
### Regenerated types
|
|
144
|
+
|
|
145
|
+
- **BREAKING - `quotas[]` on `GET /v1/accounts/{account_id}` is a different
|
|
146
|
+
array.** `quota_name`, `remaining`, `total`, `reset_time` and
|
|
147
|
+
`recommended_throttle_hint` are gone. Each row now carries `budget_row`,
|
|
148
|
+
`kind` (`debit` / `tally` / `gauge`), `window`, `window_start`, `resets_at`,
|
|
149
|
+
`used`, `ceiling`, `effective_ceiling`, `band`, `posture`, `over_default`,
|
|
150
|
+
`halt` and, on the gauge, `observed_at`. **Compare `used` against
|
|
151
|
+
`effective_ceiling`**, which is `ceiling` with the account's warm-up ramp
|
|
152
|
+
applied; on a new account the two differ by roughly a factor of seven.
|
|
153
|
+
`used` is `null` only on the gauge, and only before anything has observed
|
|
154
|
+
it. The per-minute REQUEST ceiling is deliberately not in this array.
|
|
155
|
+
- **BREAKING - `message_id` is `string | string[] | null`** on `startChat`,
|
|
156
|
+
`sendMessage`, `sendInMail` and the company chat reply, where it was
|
|
157
|
+
`string`. `null` when no message was actually sent, an array when the
|
|
158
|
+
message was delivered as several, one per attachment. Confirm a send with
|
|
159
|
+
`chat_id`, which is always present.
|
|
160
|
+
- Chat-search results: `type` gains `channel`. The generated comment used to
|
|
161
|
+
say search results are never channels; that is no longer true, so an
|
|
162
|
+
exhaustive `switch` over `type` needs a third arm.
|
|
163
|
+
- Additive: `GET`/`PATCH /v1/{account_id}/safety-policy`,
|
|
164
|
+
`GET /v1/{account_id}/safety-events`, the four retained-event operations
|
|
165
|
+
under `/v1/{account_id}/events`, a `SafetyWarning` component reachable as
|
|
166
|
+
`safety_warning` on 149 success responses, six new optional fields on the
|
|
167
|
+
shared `Error` component, `coverage` on chat search, `mode` and `max_age`
|
|
168
|
+
query params on the store-served reads, `account_states[]` on the account
|
|
169
|
+
resource, and `observed_at` / `source` / `withdrawn` becoming REQUIRED on
|
|
170
|
+
four entity reads (a response gaining guarantees, not losing them).
|
|
171
|
+
|
|
172
|
+
- **`postpublish` now notices a stale CLI pin.** After every `npm publish`,
|
|
173
|
+
`scripts/check-cli-notice.mjs` compares the version just published
|
|
174
|
+
against what `@curviate/cli` actually declares for `@curviate/sdk` on the
|
|
175
|
+
registry (never a dist-tag lookup, since `npm view @curviate/sdk version`
|
|
176
|
+
returns `latest` and this package's own RC/0.x releases route to `--tag
|
|
177
|
+
next`). Exits non-zero, loudly, when the CLI has not picked up the
|
|
178
|
+
release yet. It cannot undo the publish and does not try to; it replaces
|
|
179
|
+
the prior manual runbook step, which could not fail by construction
|
|
180
|
+
(the SDK-CLI drift-notice defect).
|
|
181
|
+
- `check:clean` now flags a committed npm auth token (`npm_`-prefixed
|
|
182
|
+
shape), and `.npmrc`/`*.tgz` are gitignored. Same gap as the sibling
|
|
183
|
+
`cli` package's copy of this scanner (security-auditor F3).
|
|
11
184
|
|
|
12
185
|
## [0.24.3] - 2026-08-27
|
|
13
186
|
|
package/README.md
CHANGED
|
@@ -109,9 +109,26 @@ try {
|
|
|
109
109
|
// err.retryAfterMs tells you exactly how long to wait
|
|
110
110
|
await sleep(err.retryAfterMs ?? 5_000);
|
|
111
111
|
break;
|
|
112
|
+
case "BUDGET_EXHAUSTED":
|
|
113
|
+
// Curviate's OWN account-safety ceiling, not a request-rate limit:
|
|
114
|
+
// nothing reached LinkedIn and nothing was spent, so backing off is the
|
|
115
|
+
// wrong move. Wait until err.resetAt, or raise the setting the hint
|
|
116
|
+
// names on PATCH /v1/{account_id}/safety-policy.
|
|
117
|
+
// resetAt is null in the two cases no clock frees: the pending_invites
|
|
118
|
+
// backlog, and an InMail credit pool LinkedIn regrants on its own schedule.
|
|
119
|
+
console.warn(`${err.budgetRow} is spent until ${err.resetAt ?? "no fixed time"}`);
|
|
120
|
+
console.warn(`change ${err.safetyHint?.parameter} to lift it`);
|
|
121
|
+
break;
|
|
112
122
|
case "ACCOUNT_NOT_FOUND":
|
|
113
123
|
console.error("Account does not exist for this tenant.");
|
|
114
124
|
break;
|
|
125
|
+
case "NOT_STORED":
|
|
126
|
+
// A cache_only read the store could not answer, so nothing was fetched.
|
|
127
|
+
// The id may be perfectly good, so do not go looking for it again:
|
|
128
|
+
// re-read with mode "refill" or "auto", and on a narrowed listing drop
|
|
129
|
+
// the narrowing parameters. See "Retrieval modes" below.
|
|
130
|
+
console.warn("Nothing stored for that resource under this mode.");
|
|
131
|
+
break;
|
|
115
132
|
default:
|
|
116
133
|
if (err.retryLikelyToSucceed) {
|
|
117
134
|
// Safe to retry: server-side transient error
|
|
@@ -127,6 +144,67 @@ exported `ErrorCode` type is the complete set, so `tsc` tells you when a `switch
|
|
|
127
144
|
|
|
128
145
|
---
|
|
129
146
|
|
|
147
|
+
## Retrieval modes
|
|
148
|
+
|
|
149
|
+
Some reads can be answered from Curviate's own store instead of a live LinkedIn
|
|
150
|
+
fetch. Those reads take the same two query parameters, `mode` and `max_age`:
|
|
151
|
+
|
|
152
|
+
- `users.get()`, including `users.get("me")` (`GET /v1/{account_id}/users/{user_id}`)
|
|
153
|
+
- `messaging.getChat()` (`GET /v1/{account_id}/chats/{chat_id}`)
|
|
154
|
+
- `messaging.listMessages()` (`GET /v1/{account_id}/chats/{chat_id}/messages`)
|
|
155
|
+
|
|
156
|
+
No other read accepts them, so do not pass them elsewhere. These two keys are
|
|
157
|
+
refused rather than ignored: sending either to a read that does not declare it
|
|
158
|
+
is a `400`, and so is sending either one twice. A read that accepted
|
|
159
|
+
`mode=cache_only` and then called LinkedIn anyway would break the one guarantee
|
|
160
|
+
that parameter makes, so neither case is resolved quietly.
|
|
161
|
+
|
|
162
|
+
| `mode` | What the read does |
|
|
163
|
+
| --- | --- |
|
|
164
|
+
| `auto` (default) | serves a stored copy while it is inside the resource's freshness threshold, otherwise fetches |
|
|
165
|
+
| `live` | always fetches |
|
|
166
|
+
| `refill` | serves a stored copy at any age, and fetches once when this read has none |
|
|
167
|
+
| `cache_only` | never fetches, and throws `NOT_STORED` when the store cannot answer |
|
|
168
|
+
|
|
169
|
+
`max_age` is the mechanism the first three are presets over: the oldest stored
|
|
170
|
+
copy, in seconds, the read will accept. It overrides them in both directions,
|
|
171
|
+
and `max_age: 0` is the same as `mode: "live"`. It cannot be combined with
|
|
172
|
+
`cache_only`, whose guarantee is not a freshness threshold; that pair is
|
|
173
|
+
rejected with `INVALID_REQUEST` rather than one of the two being quietly
|
|
174
|
+
dropped.
|
|
175
|
+
|
|
176
|
+
```ts
|
|
177
|
+
// Serve whatever is stored, at any age; reach LinkedIn only if nothing is.
|
|
178
|
+
const chat = await acc.messaging.getChat("chat_1", { mode: "refill" });
|
|
179
|
+
|
|
180
|
+
// Never reach LinkedIn. Throws NOT_STORED when the store holds nothing.
|
|
181
|
+
const profile = await acc.users.get("me", { mode: "cache_only" });
|
|
182
|
+
|
|
183
|
+
// Accept a stored copy up to five minutes old, fetch otherwise.
|
|
184
|
+
const page = await acc.messaging.listMessages("chat_1", { max_age: 300 });
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Every one of these responses carries fields that say what you are holding:
|
|
188
|
+
|
|
189
|
+
- `source` is `"store"` or `"live"`, and `observed_at` is when the data was seen
|
|
190
|
+
on LinkedIn. A stored answer can carry less than a live one, because some
|
|
191
|
+
fields are dropped before anything is written, so `source: "store"` is how you
|
|
192
|
+
know to ask again with `mode: "live"` when a field you need is missing.
|
|
193
|
+
- `withdrawn` is always present, never inferred from a missing field. `true`
|
|
194
|
+
means LinkedIn has said the resource is gone, such as a removed profile, and
|
|
195
|
+
the answer you are holding is the copy Curviate still has: do not act on it.
|
|
196
|
+
`withdrawn_at` rides along when it is `true`. This is the field to branch on
|
|
197
|
+
before messaging or acting on anything served from the store, and `refill`,
|
|
198
|
+
which serves a copy at any age, is the mode most likely to hand you one.
|
|
199
|
+
|
|
200
|
+
`NOT_STORED` under `cache_only` does not always mean the resource is unknown.
|
|
201
|
+
On `listMessages` it also fires when the request narrows the page in a way the
|
|
202
|
+
stored copy cannot reproduce, so a fully stored chat can refuse a narrowed
|
|
203
|
+
`cache_only` read. Re-read without the narrowing parameters, or with a mode that
|
|
204
|
+
may fetch.
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
130
208
|
## Cursor pagination
|
|
131
209
|
|
|
132
210
|
Resources that return lists support cursor pagination. `curviate.paginate()` is an async iterator that follows the `cursor` field automatically, so you pull items one at a time without managing cursors:
|