@curviate/sdk 0.25.0 → 0.27.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 +161 -0
- package/README.md +68 -0
- package/dist/index.d.ts +89 -62
- package/dist/index.js +19 -3
- package/package.json +1 -1
- package/src/generated/types.ts +74 -56
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,167 @@ Versioning: semantic. Minor for additive changes, patch for bug fixes; no stabil
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
+
## [0.27.0] - 2026-09-06
|
|
11
|
+
|
|
12
|
+
Fixture regenerated against the deployed production document
|
|
13
|
+
(`https://api.curviate.com`, `server_git_sha 2a86542f...`, 125 paths).
|
|
14
|
+
**BREAKING**: the `message.delivered` webhook event is gone from the catalogue,
|
|
15
|
+
from `CurviateEvent`, and from the events a subscription can be created with.
|
|
16
|
+
Everything else here is additive or description-only. Pre-1.0 a breaking change
|
|
17
|
+
ships as a minor, so a caret range on `0.26.x` will not pick this up.
|
|
18
|
+
|
|
19
|
+
### BREAKING
|
|
20
|
+
|
|
21
|
+
- **`message.delivered` is removed from the webhook event catalogue.** The
|
|
22
|
+
platform never emitted it, so no `event.event === 'message.delivered'` branch
|
|
23
|
+
has ever run: the event was subscribable and undeliverable. Two surfaces lose
|
|
24
|
+
the member.
|
|
25
|
+
- `CurviateEvent` (hand-written, `src/webhooks.ts`) drops its
|
|
26
|
+
`{ event: "message.delivered"; data: MessagePayload }` arm, going from 25
|
|
27
|
+
arms to 24. A `switch` on `event.event` that still lists it stops
|
|
28
|
+
compiling, and that arm was dead code in every build it ever shipped in.
|
|
29
|
+
- The generated create-events enum, `events` on the `messaging` variant of
|
|
30
|
+
the `POST /v1/webhooks` request body
|
|
31
|
+
(`operations["postV1Webhooks"]["requestBody"]`), goes from 8 members to 7.
|
|
32
|
+
Passing `"message.delivered"` to a create call is now a type error, and the
|
|
33
|
+
server rejects the value.
|
|
34
|
+
|
|
35
|
+
The two are pinned to each other by an exact two-way type equality in
|
|
36
|
+
`test/webhooks.constructEvent.test.ts`, so they cannot drift apart: that pin
|
|
37
|
+
is what failed first on this regeneration.
|
|
38
|
+
|
|
39
|
+
`GET /v1/webhooks/events` now reports 27 events, messaging 7, where it
|
|
40
|
+
reported 28 and 8.
|
|
41
|
+
|
|
42
|
+
**The read path is untyped and deliberately unaffected.** `events` on a
|
|
43
|
+
webhook returned by `GET /v1/webhooks` or `GET /v1/webhooks/{id}` is
|
|
44
|
+
`string[]`, not the create enum. A subscription created before 2026-09-06 may
|
|
45
|
+
still echo `message.delivered` in its stored `events` until the server-side
|
|
46
|
+
cleanup migration lands; it was never delivered. Reading such a subscription
|
|
47
|
+
back therefore neither breaks a build nor throws, and no migration is
|
|
48
|
+
required of a consumer that only lists webhooks.
|
|
49
|
+
|
|
50
|
+
**The replacement is a read, not an event**: `is_delivered` on the message
|
|
51
|
+
resource.
|
|
52
|
+
|
|
53
|
+
### Regenerated types
|
|
54
|
+
|
|
55
|
+
- **BREAKING for an exhaustive `switch` - `notices[].code` gains
|
|
56
|
+
`EXPANSION_WITHHELD_CEILING`.** On the same ten chat and message reads that
|
|
57
|
+
accept `expand=public_identifier`, alongside `EXPANSION_LIMIT_REACHED` and
|
|
58
|
+
`EXPANSION_WITHHELD_ACTIVITY_WINDOW`. The expansion was withheld because the
|
|
59
|
+
account is at an account-safety ceiling, not because the page ran out of
|
|
60
|
+
lookup budget and not because the account is outside its activity window.
|
|
61
|
+
Same consequence for the reader as its two siblings, `public_identifier` is
|
|
62
|
+
`null` for a reason that is not "no public profile", and a different remedy,
|
|
63
|
+
which is why it is a separate code rather than a reuse. Additive on the wire:
|
|
64
|
+
only a `switch` with a `never`-typed default arm stops compiling.
|
|
65
|
+
- Description-only, no type change:
|
|
66
|
+
- `SafetyWarning` now states that `hint.message` is the one field whose text
|
|
67
|
+
differs from the `BUDGET_EXHAUSTED` refusal. On a warn-posture ceiling
|
|
68
|
+
breach it says the action went through and how far past the ceiling the row
|
|
69
|
+
now stands, rather than telling the caller what was refused. The shape is
|
|
70
|
+
unchanged, so no branch moves; the string is longer and more specific.
|
|
71
|
+
- The `502` response documents that `retry_likely_to_succeed` is what
|
|
72
|
+
separates its two cases: a temporary upstream error is worth retrying, an
|
|
73
|
+
upstream response that could not be interpreted is not, and the latter
|
|
74
|
+
carries `retry_hint: {"kind": "never"}`. No new field, and the SDK's own
|
|
75
|
+
retry set is unchanged in this release.
|
|
76
|
+
- The `notices` description on the ten expansion reads now lists the ceiling
|
|
77
|
+
case as a third reason a person can go unresolved.
|
|
78
|
+
|
|
79
|
+
## [0.26.0] - 2026-09-06
|
|
80
|
+
|
|
81
|
+
Fixture regenerated against the deployed production document
|
|
82
|
+
(`https://api.curviate.com`, `server_git_sha bd795601...`, 125 paths).
|
|
83
|
+
**BREAKING at the type level** for a consumer that reads `row` off an error
|
|
84
|
+
or a safety warning as a `string`: it is now `string | null` on both. Three
|
|
85
|
+
response enums also widen, which reds an exhaustive `switch` with a `never`
|
|
86
|
+
default. Everything else in this release is additive.
|
|
87
|
+
|
|
88
|
+
### Added
|
|
89
|
+
|
|
90
|
+
- **`NOT_STORED` in the error taxonomy.** The `422` a read gets when
|
|
91
|
+
`mode=cache_only` (or `refill` on a resource that cannot be filled) finds
|
|
92
|
+
nothing in Curviate's store, so the API refuses rather than reaching
|
|
93
|
+
LinkedIn. It was missing from `ERROR_CODES`, so it decoded to `INTERNAL`:
|
|
94
|
+
a caller could not branch on it, and, the harmful half, `INTERNAL` is
|
|
95
|
+
retryable, so a `cache_only` miss was retried on a GET against an answer
|
|
96
|
+
that cannot change until the caller picks another mode. It is now decoded to
|
|
97
|
+
itself and is deliberately absent from the retryable set. Not the same as
|
|
98
|
+
`RESOURCE_NOT_FOUND`: the resource may exist and this API simply holds no
|
|
99
|
+
copy, so the fix is another mode, not another id. `cache_only` is the mode
|
|
100
|
+
that raises it on these reads, and `refill` or `auto` is the remedy.
|
|
101
|
+
|
|
102
|
+
**This reds `@curviate/cli`'s exit-code exhaustiveness guard, by design.**
|
|
103
|
+
That guard iterates the exported `ERROR_CODES` array rather than a copy of
|
|
104
|
+
it, which is the whole reason the array is exported, so a code added here
|
|
105
|
+
with no exit-code mapping is supposed to fail it. Map `NOT_STORED` there when
|
|
106
|
+
bumping the pin.
|
|
107
|
+
- **`messaging.getChat(chatId, params?)` takes the retrieval query.**
|
|
108
|
+
`GET /v1/{account_id}/chats/{chat_id}` declares `expand`, `mode` and
|
|
109
|
+
`max_age`; the method sent no query at all, so a chat read was the one
|
|
110
|
+
store-servable read with no retrieval ladder. `ChatGetQuery` is derived from
|
|
111
|
+
the generated `paths` types, like every other query type here.
|
|
112
|
+
`users.get()` and `messaging.listMessages()` already carried the pair and
|
|
113
|
+
are unchanged; the README now documents all three.
|
|
114
|
+
|
|
115
|
+
### Regenerated types
|
|
116
|
+
|
|
117
|
+
- **BREAKING - `row` is nullable on both `Error` and `SafetyWarning`.**
|
|
118
|
+
`Error.row` goes `string | undefined` to `string | null | undefined`, and
|
|
119
|
+
`SafetyWarning.row` - which is REQUIRED, so it is the one that will actually
|
|
120
|
+
break a build - goes `string` to `string | null`. `null` says the breach
|
|
121
|
+
names no row: an `activity_window` refusal on an action that spends no
|
|
122
|
+
budget of its own has an hour to answer for and no counter. Read
|
|
123
|
+
`hint.parameter` in that case; it names `posture`. A consumer that assigned
|
|
124
|
+
`.row` to a `string` or passed it somewhere non-null now has a type error,
|
|
125
|
+
and that error is correct - the runtime value was always going to be `null`
|
|
126
|
+
once the server started sending it. The SDK's own `CurviateError.budgetRow`
|
|
127
|
+
is UNCHANGED (`string | undefined`): `transport.ts` maps the wire field with
|
|
128
|
+
a `typeof === "string"` guard, so a `null` row arrives as an absent
|
|
129
|
+
`budgetRow`, which is what "no row to name" already meant on that surface.
|
|
130
|
+
- **BREAKING for an exhaustive `switch` - three response enums widen.** Each
|
|
131
|
+
gains a member, so a `switch` with a `never`-typed default arm stops
|
|
132
|
+
compiling. None of them removes a member, so a `switch` with a real default
|
|
133
|
+
keeps working:
|
|
134
|
+
- `GET /v1/{account_id}/safety-events` `items[].reason` gains
|
|
135
|
+
`activity_window` alongside `budget_exhausted` and `rate_limited`.
|
|
136
|
+
- `GET /v1/{account_id}/safety-events` `items[].budget_row` gains `"*"`, the
|
|
137
|
+
wildcard for an event that is not scoped to one row.
|
|
138
|
+
- `notices[].code` on the ten chat and message reads that accept
|
|
139
|
+
`expand=public_identifier` gains `EXPANSION_WITHHELD_ACTIVITY_WINDOW`
|
|
140
|
+
alongside `EXPANSION_LIMIT_REACHED`: the expansion was refused because the
|
|
141
|
+
account is outside its activity window, not because the page ran out of
|
|
142
|
+
lookup budget. Same consequence for the reader either way -
|
|
143
|
+
`public_identifier` is `null` for reasons that are not "no public profile"
|
|
144
|
+
- but a different fix, so it is a separate code. The `notices` key itself
|
|
145
|
+
is not new.
|
|
146
|
+
- **Additive - `interface_ceiling` and `effective_interface_ceiling` on every
|
|
147
|
+
budget row.** Optional, `number | null`. They appear on `quotas[]` from
|
|
148
|
+
`GET /v1/accounts/{account_id}` and on the policy rows from
|
|
149
|
+
`GET`/`PATCH /v1/{account_id}/safety-policy`. What a call through the
|
|
150
|
+
matching ELEVATED interface (`/sales-navigator/...`, `/recruiter/...`) is
|
|
151
|
+
held to on that row; `null` wherever the row and limit profile have no such
|
|
152
|
+
figure, which is every row on `basic` and `premium`. `ceiling`,
|
|
153
|
+
`effective_ceiling`, `band` and `over_default` all remain the STANDARD
|
|
154
|
+
figure, so a row can report `band: "over"` while an elevated call still
|
|
155
|
+
succeeds. Read `effective_interface_ceiling`, not `interface_ceiling`, for
|
|
156
|
+
the same reason `effective_ceiling` and not `ceiling` answers that on the
|
|
157
|
+
standard side. On the `PATCH` request body both are typed `unknown` and
|
|
158
|
+
documented READ-ONLY: sent back they are accepted and ignored, so a read can
|
|
159
|
+
round-trip unedited.
|
|
160
|
+
- **Additive - `activity_window` on the `PATCH /v1/{account_id}/safety-policy`
|
|
161
|
+
request body**, typed `unknown` and documented NOT ACCEPTED. Send the
|
|
162
|
+
settable subfields instead: top-level `timezone`, and per row
|
|
163
|
+
`activity_window_start`, `activity_window_end`, `activity_window_timezone`,
|
|
164
|
+
`activity_window_applies_to`.
|
|
165
|
+
- Description-only, no type change: `reset_at` on `Error` and `SafetyWarning`
|
|
166
|
+
now states that it is the window roll on a `ceiling` refusal and the next
|
|
167
|
+
window open on an `activity_window` one, and restates the two cases where no
|
|
168
|
+
clock frees the account (the `pending_invites` gauge, and InMail credits).
|
|
169
|
+
The chat-search `q` parameter documents that a 1-2 character term is slower.
|
|
170
|
+
|
|
10
171
|
## [0.25.0] - 2026-09-05
|
|
11
172
|
|
|
12
173
|
**BREAKING** for a consumer that pinned the old type declarations: `quotas[]`
|
package/README.md
CHANGED
|
@@ -122,6 +122,13 @@ try {
|
|
|
122
122
|
case "ACCOUNT_NOT_FOUND":
|
|
123
123
|
console.error("Account does not exist for this tenant.");
|
|
124
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;
|
|
125
132
|
default:
|
|
126
133
|
if (err.retryLikelyToSucceed) {
|
|
127
134
|
// Safe to retry: server-side transient error
|
|
@@ -137,6 +144,67 @@ exported `ErrorCode` type is the complete set, so `tsc` tells you when a `switch
|
|
|
137
144
|
|
|
138
145
|
---
|
|
139
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
|
+
|
|
140
208
|
## Cursor pagination
|
|
141
209
|
|
|
142
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:
|