@zyphr-dev/node-sdk 0.1.45 → 0.1.47

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zyphr-dev/node-sdk",
3
- "version": "0.1.45",
3
+ "version": "0.1.47",
4
4
  "description": "Official Zyphr SDK for Node.js, React, and React Native",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
package/src/clientAuth.ts CHANGED
@@ -28,7 +28,8 @@
28
28
  * const login = await zyphr.auth.login.loginEndUser({ ... });
29
29
  * zyphr.setAccessToken(login.data.tokens.access_token);
30
30
  *
31
- * const me = await zyphr.auth.profile.getEndUser(zyphr.authedRequest());
31
+ * // The stored token is applied automatically to user-scoped calls:
32
+ * const me = await zyphr.auth.profile.getEndUser();
32
33
  * ```
33
34
  */
34
35
 
@@ -60,7 +61,14 @@ export interface ZyphrClientOptions {
60
61
  applicationKey: string;
61
62
  /** Override the base API URL (defaults to https://api.zyphr.dev/v1). */
62
63
  baseUrl?: string;
63
- /** Optional end-user access token, if you already have a session on construction. */
64
+ /**
65
+ * Optional end-user access token, if you already have a session on
66
+ * construction. Once set (here or via {@link ZyphrClient.setAccessToken}), it
67
+ * is applied automatically as `Authorization: Bearer` to every user-scoped
68
+ * call — you no longer need to pass {@link ZyphrClient.authedRequest} for
69
+ * authentication (though authedRequest still works and a token passed to it
70
+ * overrides the stored one).
71
+ */
64
72
  accessToken?: string;
65
73
  }
66
74
 
@@ -157,10 +165,18 @@ export class ZyphrClient {
157
165
 
158
166
  const config = new Configuration({
159
167
  basePath: options.baseUrl,
160
- // The only credential this client sends automatically is the publishable
161
- // application key. End-user Bearer tokens are attached per-request via
162
- // authedRequest() so token rotation doesn't require rebuilding the client.
168
+ // The publishable application key is sent automatically on every call.
163
169
  apiKey: (name: string) => (name === 'X-Application-Key' ? this.applicationKey : ''),
170
+ // The stored end-user access token is also applied automatically: any
171
+ // user-scoped generated method (which authenticates the EndUserToken
172
+ // scheme) invokes this callback and attaches `Authorization: Bearer`.
173
+ // The callback reads `this.accessToken` at call time, so setAccessToken()
174
+ // takes effect on subsequent calls without rebuilding the client. When no
175
+ // token is set it returns '', and the generated code adds no header — so
176
+ // anonymous endpoints (login, register, password-reset) stay unauthenticated.
177
+ // An explicit token passed to authedRequest() sets the header directly in
178
+ // RequestInit, which overrides this callback.
179
+ accessToken: async () => this.accessToken ?? '',
164
180
  middleware: [errorMiddleware],
165
181
  });
166
182
 
@@ -182,7 +198,7 @@ export class ZyphrClient {
182
198
  this.devices = new DevicesApi(config);
183
199
  }
184
200
 
185
- /** Store the end-user access token for subsequent authedRequest() calls. */
201
+ /** Store the end-user access token; it is applied automatically to subsequent user-scoped calls. */
186
202
  setAccessToken(accessToken: string | undefined): void {
187
203
  this.accessToken = accessToken;
188
204
  }
@@ -195,17 +211,33 @@ export class ZyphrClient {
195
211
  /**
196
212
  * Build request overrides that authenticate as the current end user: the
197
213
  * stored Bearer token plus the publishable application key required by
198
- * user-scoped endpoints. Pass an explicit token to override the stored one.
214
+ * user-scoped endpoints.
215
+ *
216
+ * The stored token is now applied automatically to user-scoped calls, so this
217
+ * is no longer required for authentication. It remains useful to authenticate
218
+ * a single call with a different token — a token passed here sets the header
219
+ * directly in RequestInit and overrides the stored token for that call.
199
220
  *
200
221
  * @example
201
222
  * ```ts
202
- * const me = await zyphr.auth.profile.getEndUser(zyphr.authedRequest());
223
+ * // Authenticate one call as a different user, without changing stored state:
224
+ * const me = await zyphr.auth.profile.getEndUser(zyphr.authedRequest(otherToken));
203
225
  * ```
204
226
  */
205
227
  authedRequest(accessToken?: string): RequestInit {
206
228
  const token = accessToken ?? this.accessToken;
229
+ // The runtime REPLACES the whole headers object with this initOverride (it
230
+ // does not deep-merge — see createFetchParams), so every header the request
231
+ // needs must be re-listed here or it is dropped for this call:
232
+ // - X-Application-Key: the app key the Configuration would otherwise add.
233
+ // - Content-Type: application/json: the generated method sets this for
234
+ // body-carrying calls; without it a POST via authedRequest() goes out
235
+ // with no JSON content-type, the server parses no fields, and returns a
236
+ // misleading validation error (e.g. "current_password is required").
237
+ // Harmless on GETs (no body); required for any mutating call. (sc-7631)
207
238
  const headers: Record<string, string> = {
208
239
  'X-Application-Key': this.applicationKey,
240
+ 'Content-Type': 'application/json',
209
241
  };
210
242
  if (token) {
211
243
  headers.Authorization = `Bearer ${token}`;
@@ -134,7 +134,7 @@ export interface EmailsApiInterface {
134
134
  /**
135
135
  * Retrieve a paginated list of sent emails
136
136
  * @summary List emails
137
- * @param {'pending' | 'queued' | 'sent' | 'delivered' | 'failed' | 'bounced' | 'rejected'} [status]
137
+ * @param {'pending' | 'queued' | 'sent' | 'delivered' | 'failed' | 'bounced' | 'rejected' | 'suppressed'} [status]
138
138
  * @param {string} [tag]
139
139
  * @param {number} [limit]
140
140
  * @param {number} [offset]
@@ -450,6 +450,7 @@ export const ListEmailsStatusEnum = {
450
450
  DELIVERED: 'delivered',
451
451
  FAILED: 'failed',
452
452
  BOUNCED: 'bounced',
453
- REJECTED: 'rejected'
453
+ REJECTED: 'rejected',
454
+ SUPPRESSED: 'suppressed'
454
455
  } as const;
455
456
  export type ListEmailsStatusEnum = typeof ListEmailsStatusEnum[keyof typeof ListEmailsStatusEnum];
@@ -57,6 +57,13 @@ export interface EmailMessage {
57
57
  * @memberof EmailMessage
58
58
  */
59
59
  status?: EmailMessageStatusEnum;
60
+ /**
61
+ * Why the message is in its current status, when applicable. For a `suppressed` row this is the suppression reason (e.g. `recipient_not_allowlisted` when blocked by the auth application's test_recipients allowlist). Null for normal sends.
62
+ *
63
+ * @type {string}
64
+ * @memberof EmailMessage
65
+ */
66
+ statusReason?: string | null;
60
67
  /**
61
68
  *
62
69
  * @type {Array<string>}
@@ -118,7 +125,8 @@ export const EmailMessageStatusEnum = {
118
125
  DELIVERED: 'delivered',
119
126
  FAILED: 'failed',
120
127
  BOUNCED: 'bounced',
121
- REJECTED: 'rejected'
128
+ REJECTED: 'rejected',
129
+ SUPPRESSED: 'suppressed'
122
130
  } as const;
123
131
  export type EmailMessageStatusEnum = typeof EmailMessageStatusEnum[keyof typeof EmailMessageStatusEnum];
124
132
 
@@ -145,6 +153,7 @@ export function EmailMessageFromJSONTyped(json: any, ignoreDiscriminator: boolea
145
153
  'to': json['to'] == null ? undefined : EmailAddressFromJSON(json['to']),
146
154
  'subject': json['subject'] == null ? undefined : json['subject'],
147
155
  'status': json['status'] == null ? undefined : json['status'],
156
+ 'statusReason': json['status_reason'] == null ? undefined : json['status_reason'],
148
157
  'tags': json['tags'] == null ? undefined : json['tags'],
149
158
  'queuedAt': json['queued_at'] == null ? undefined : (new Date(json['queued_at'])),
150
159
  'sentAt': json['sent_at'] == null ? undefined : (new Date(json['sent_at'])),
@@ -172,6 +181,7 @@ export function EmailMessageToJSONTyped(value?: EmailMessage | null, ignoreDiscr
172
181
  'to': EmailAddressToJSON(value['to']),
173
182
  'subject': value['subject'],
174
183
  'status': value['status'],
184
+ 'status_reason': value['statusReason'],
175
185
  'tags': value['tags'],
176
186
  'queued_at': value['queuedAt'] == null ? undefined : ((value['queuedAt']).toISOString()),
177
187
  'sent_at': value['sentAt'] == null ? undefined : ((value['sentAt'] as any).toISOString()),
@@ -27,6 +27,7 @@ export const WebhookEventType = {
27
27
  EMAIL_OPENED: 'email.opened',
28
28
  EMAIL_CLICKED: 'email.clicked',
29
29
  EMAIL_FAILED: 'email.failed',
30
+ EMAIL_SUPPRESSED: 'email.suppressed',
30
31
  EMAIL_RECEIVED: 'email.received',
31
32
  PUSH_SENT: 'push.sent',
32
33
  PUSH_DELIVERED: 'push.delivered',