@doist/comms-sdk 3.6.0 → 3.8.1

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.
Files changed (37) hide show
  1. package/dist/esm/clients/channels-client.js +1 -1
  2. package/dist/esm/clients/comments-client.js +1 -1
  3. package/dist/esm/clients/conversation-messages-client.js +1 -1
  4. package/dist/esm/clients/conversations-client.js +1 -1
  5. package/dist/esm/clients/groups-client.js +1 -1
  6. package/dist/esm/clients/hooks-client.js +1 -1
  7. package/dist/esm/clients/inbox-client.js +1 -1
  8. package/dist/esm/clients/threads-client.js +1 -1
  9. package/dist/esm/clients/users-client.js +1 -1
  10. package/dist/esm/clients/workspace-users-client.js +1 -1
  11. package/dist/esm/clients/workspaces-client.js +1 -1
  12. package/dist/esm/consts/endpoints.js +3 -3
  13. package/dist/esm/transport/http-client.js +2 -1
  14. package/dist/esm/types/api-version.js +2 -2
  15. package/dist/esm/utils/headers.js +29 -0
  16. package/dist/esm/utils/multipart-upload.js +5 -6
  17. package/dist/types/clients/add-comment-helper.d.ts +2 -2
  18. package/dist/types/clients/base-client.d.ts +5 -5
  19. package/dist/types/clients/channels-client.d.ts +1 -1
  20. package/dist/types/clients/comments-client.d.ts +1 -1
  21. package/dist/types/clients/conversation-messages-client.d.ts +1 -1
  22. package/dist/types/clients/conversations-client.d.ts +1 -1
  23. package/dist/types/clients/groups-client.d.ts +1 -1
  24. package/dist/types/clients/hooks-client.d.ts +1 -1
  25. package/dist/types/clients/inbox-client.d.ts +1 -1
  26. package/dist/types/clients/threads-client.d.ts +1 -1
  27. package/dist/types/clients/users-client.d.ts +1 -1
  28. package/dist/types/clients/workspace-users-client.d.ts +1 -1
  29. package/dist/types/clients/workspaces-client.d.ts +1 -1
  30. package/dist/types/comms-api.d.ts +9 -4
  31. package/dist/types/consts/endpoints.d.ts +3 -3
  32. package/dist/types/transport/http-client.d.ts +2 -2
  33. package/dist/types/types/api-version.d.ts +1 -1
  34. package/dist/types/types/http.d.ts +6 -0
  35. package/dist/types/utils/headers.d.ts +13 -0
  36. package/dist/types/utils/multipart-upload.d.ts +4 -4
  37. package/package.json +1 -1
@@ -6,7 +6,7 @@ import { resolveCreateId } from '../utils/uuidv7.js';
6
6
  import { BaseClient } from './base-client.js';
7
7
  export const ChannelListSchema = z.array(ChannelSchema);
8
8
  /**
9
- * Client for `/api/v1/channels/`. The SDK auto-generates an `id` on
9
+ * Client for `/api/v1.0/channels/`. The SDK auto-generates an `id` on
10
10
  * `createChannel` when the caller doesn't supply one — pass your own `id`
11
11
  * to keep an optimistic-UI ID stable through the round-trip.
12
12
  */
@@ -6,7 +6,7 @@ import { addCommentRequest } from './add-comment-helper.js';
6
6
  import { BaseClient } from './base-client.js';
7
7
  export const CommentListSchema = z.array(CommentSchema);
8
8
  /**
9
- * Client for `/api/v1/comments/`. The SDK auto-generates the comment `id`
9
+ * Client for `/api/v1.0/comments/`. The SDK auto-generates the comment `id`
10
10
  * on `createComment` when the caller doesn't supply one.
11
11
  */
12
12
  export class CommentsClient extends BaseClient {
@@ -6,7 +6,7 @@ import { resolveCreateId } from '../utils/uuidv7.js';
6
6
  import { BaseClient } from './base-client.js';
7
7
  export const ConversationMessageListSchema = z.array(ConversationMessageSchema);
8
8
  /**
9
- * Client for `/api/v1/conversation_messages/`. The SDK auto-generates the
9
+ * Client for `/api/v1.0/conversation_messages/`. The SDK auto-generates the
10
10
  * message `id` on `createMessage` when the caller doesn't supply one.
11
11
  */
12
12
  export class ConversationMessagesClient extends BaseClient {
@@ -10,7 +10,7 @@ const GetUnreadResponseSchema = z.object({
10
10
  version: z.number().int(),
11
11
  });
12
12
  /**
13
- * Client for `/api/v1/conversations/`. `getOrCreate` requires an `id` (the
13
+ * Client for `/api/v1.0/conversations/`. `getOrCreate` requires an `id` (the
14
14
  * SDK auto-generates one for new conversations); the backend dedupes on
15
15
  * `userIds`, so an existing conversation will be returned with its own
16
16
  * already-assigned `id` and your generated one is silently dropped.
@@ -6,7 +6,7 @@ import { resolveCreateId } from '../utils/uuidv7.js';
6
6
  import { BaseClient } from './base-client.js';
7
7
  export const GroupListSchema = z.array(GroupSchema);
8
8
  /**
9
- * Client for `/api/v1/groups/`. The broadcast markers `EVERYONE` /
9
+ * Client for `/api/v1.0/groups/`. The broadcast markers `EVERYONE` /
10
10
  * `EVERYONE_IN_THREAD` are NOT addressable through these endpoints — they
11
11
  * only appear as members of `direct_group_mentions` / `groups` lists on
12
12
  * thread/comment writes.
@@ -3,7 +3,7 @@ import { request } from '../transport/http-client.js';
3
3
  import { HookSubscribeResponseSchema, StatusOkSchema, } from '../types/entities.js';
4
4
  import { BaseClient } from './base-client.js';
5
5
  /**
6
- * Client for `/api/v1/hooks/`. REST hooks require Todoist OAuth bearer tokens
6
+ * Client for `/api/v1.0/hooks/`. REST hooks require Todoist OAuth bearer tokens
7
7
  * that include Comms audience; session tokens are rejected by the backend.
8
8
  */
9
9
  export class HooksClient extends BaseClient {
@@ -4,7 +4,7 @@ import { request } from '../transport/http-client.js';
4
4
  import { createInboxThreadSchema, InboxThreadSchema } from '../types/entities.js';
5
5
  import { BaseClient } from './base-client.js';
6
6
  export const InboxThreadListSchema = z.array(InboxThreadSchema);
7
- /** Client for `/api/v1/inbox/`. */
7
+ /** Client for `/api/v1.0/inbox/`. */
8
8
  export class InboxClient extends BaseClient {
9
9
  constructor() {
10
10
  super(...arguments);
@@ -13,7 +13,7 @@ const GetUnreadResponseSchema = z.object({
13
13
  inboxUnread: z.number().int().nullable().optional(),
14
14
  });
15
15
  /**
16
- * Client for `/api/v1/threads/`. The SDK auto-generates the thread `id` on
16
+ * Client for `/api/v1.0/threads/`. The SDK auto-generates the thread `id` on
17
17
  * `createThread` when the caller doesn't supply one.
18
18
  */
19
19
  export class ThreadsClient extends BaseClient {
@@ -3,7 +3,7 @@ import { request } from '../transport/http-client.js';
3
3
  import { UserSchema } from '../types/entities.js';
4
4
  import { BaseClient } from './base-client.js';
5
5
  /**
6
- * Client for the `/api/v1/users/` endpoints. Authentication flows through
6
+ * Client for the `/api/v1.0/users/` endpoints. Authentication flows through
7
7
  * Todoist-ID; `register` / `login` / `loginWithGoogle` / `loginWithToken` /
8
8
  * `loginWithTodoist` are the available entry points.
9
9
  */
@@ -2,7 +2,7 @@ import { request } from '../transport/http-client.js';
2
2
  import { VisibleWorkspaceUserSchema, WorkspaceUserSchema, } from '../types/entities.js';
3
3
  import { BaseClient } from './base-client.js';
4
4
  /**
5
- * Client for `/api/v1/workspace_users/`. The backend's `add` endpoint
5
+ * Client for `/api/v1.0/workspace_users/`. The backend's `add` endpoint
6
6
  * rejects non-empty `name` and `channelIds` — set neither.
7
7
  */
8
8
  export class WorkspaceUsersClient extends BaseClient {
@@ -5,7 +5,7 @@ import { ChannelSchema, createChannelSchema, WorkspaceSchema, } from '../types/e
5
5
  import { BaseClient } from './base-client.js';
6
6
  export const ChannelListSchema = z.array(ChannelSchema);
7
7
  /**
8
- * Client for `/api/v1/workspaces/`. Workspace IDs are integers. The backend
8
+ * Client for `/api/v1.0/workspaces/`. Workspace IDs are integers. The backend
9
9
  * currently rejects any `color` other than `1` on add/update.
10
10
  */
11
11
  export class WorkspacesClient extends BaseClient {
@@ -4,11 +4,11 @@ const BASE_URI = 'https://comms.todoist.com';
4
4
  * Gets the base URI for Comms API requests.
5
5
  *
6
6
  * Preserves any path component on `domainBase` so callers can route through
7
- * a proxy (e.g. `https://proxy.example.com/comms` → `.../comms/api/v1/`).
7
+ * a proxy (e.g. `https://proxy.example.com/comms` → `.../comms/api/v1.0/`).
8
8
  *
9
- * @param version - API version. Defaults to 'v1'.
9
+ * @param version - API version. Defaults to 'v1.0'.
10
10
  * @param domainBase - Custom domain base URL. Defaults to Comms' API domain.
11
- * @returns Complete base URI with trailing slash (e.g., 'https://comms.todoist.com/api/v1/')
11
+ * @returns Complete base URI with trailing slash (e.g., 'https://comms.todoist.com/api/v1.0/')
12
12
  */
13
13
  export function getCommsBaseUri(version = DEFAULT_API_VERSION, domainBase = BASE_URI) {
14
14
  const base = domainBase.endsWith('/') ? domainBase : `${domainBase}/`;
@@ -1,4 +1,5 @@
1
1
  import { snakeCaseKeys } from '../utils/case-conversion.js';
2
+ import { mergeHeaders, resolveCustomHeaders } from '../utils/headers.js';
2
3
  import { fetchWithRetry } from './fetch-with-retry.js';
3
4
  export function paramsSerializer(params) {
4
5
  const qs = new URLSearchParams();
@@ -25,7 +26,7 @@ function getRequestConfiguration(baseURL, apiToken, requestId, customHeaders) {
25
26
  const authHeader = apiToken ? { Authorization: getAuthHeader(apiToken) } : undefined;
26
27
  const requestIdHeader = requestId ? { 'X-Request-Id': requestId } : undefined;
27
28
  // Custom headers sit below auth and request id so they can never clobber them.
28
- const headers = { ...defaultHeaders, ...customHeaders, ...authHeader, ...requestIdHeader };
29
+ const headers = mergeHeaders(defaultHeaders, resolveCustomHeaders(customHeaders), authHeader, requestIdHeader);
29
30
  return { baseURL, headers, timeout: 30000 };
30
31
  }
31
32
  export async function request(args) {
@@ -1,5 +1,5 @@
1
1
  /**
2
2
  * Supported Comms API versions
3
3
  */
4
- export const API_VERSIONS = ['v1'];
5
- export const DEFAULT_API_VERSION = 'v1';
4
+ export const API_VERSIONS = ['v1.0'];
5
+ export const DEFAULT_API_VERSION = 'v1.0';
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Merges header records left to right. HTTP header names are case-insensitive,
3
+ * so a later record replaces any earlier header with the same name whatever
4
+ * its case. Without this, a caller's `authorization` would sit alongside the
5
+ * SDK's `Authorization` and `fetch` would join the two into one value.
6
+ */
7
+ export function mergeHeaders(...sources) {
8
+ const merged = {};
9
+ for (const source of sources) {
10
+ if (!source)
11
+ continue;
12
+ for (const [name, value] of Object.entries(source)) {
13
+ const lowerName = name.toLowerCase();
14
+ for (const existingName of Object.keys(merged)) {
15
+ if (existingName.toLowerCase() === lowerName)
16
+ delete merged[existingName];
17
+ }
18
+ merged[name] = value;
19
+ }
20
+ }
21
+ return merged;
22
+ }
23
+ /**
24
+ * Turns the `headers` option into the record to send with one request, calling
25
+ * it when it is a function so per-request values are fresh.
26
+ */
27
+ export function resolveCustomHeaders(headers) {
28
+ return typeof headers === 'function' ? headers() : headers;
29
+ }
@@ -1,5 +1,6 @@
1
1
  import { v4 as uuid } from 'uuid';
2
2
  import { fetchWithRetry } from '../transport/fetch-with-retry.js';
3
+ import { mergeHeaders, resolveCustomHeaders } from './headers.js';
3
4
  /**
4
5
  * Determine a content-type from a filename extension. Falls back to
5
6
  * `application/octet-stream` for unknown extensions.
@@ -178,16 +179,14 @@ export async function uploadMultipartFile(args) {
178
179
  contentType: resolvedType,
179
180
  fields,
180
181
  });
181
- const headers = {
182
- ...customHeaders,
182
+ // Custom headers come first so they can never replace the SDK's own.
183
+ const headers = mergeHeaders(resolveCustomHeaders(customHeaders), {
183
184
  Authorization: `Bearer ${authToken}`,
184
185
  // The boundary is ours, so this has to be declared explicitly — `fetch`
185
186
  // only fills it in for a `FormData` body.
186
187
  'Content-Type': multipart.contentType,
187
- };
188
- if (requestId) {
189
- headers['X-Request-Id'] = requestId;
190
- }
188
+ ...(requestId ? { 'X-Request-Id': requestId } : {}),
189
+ });
191
190
  const url = new URL(endpoint, baseUrl).toString();
192
191
  const response = await fetchWithRetry(url, {
193
192
  method: 'POST',
@@ -1,11 +1,11 @@
1
1
  import { type Comment, type createCommentSchema } from '../types/entities.js';
2
- import type { CustomFetch } from '../types/http.js';
2
+ import type { CustomFetch, CustomHeaders } from '../types/http.js';
3
3
  import type { CreateCommentArgs, ThreadAction } from '../types/requests.js';
4
4
  type ClientContext = {
5
5
  baseUri: string;
6
6
  apiToken: string;
7
7
  customFetch?: CustomFetch;
8
- headers?: Record<string, string>;
8
+ headers?: CustomHeaders;
9
9
  /** Per-client Comment schema, base-bound for the returned comment's web `url`. */
10
10
  schema: ReturnType<typeof createCommentSchema>;
11
11
  };
@@ -1,16 +1,16 @@
1
1
  import type { ApiVersion } from '../types/api-version.js';
2
- import type { CustomFetch } from '../types/http.js';
2
+ import type { CustomFetch, CustomHeaders } from '../types/http.js';
3
3
  export type ClientConfig = {
4
4
  /** API token for authentication */
5
5
  apiToken: string;
6
6
  /** Optional custom base URL. If not provided, uses the default Comms API URL */
7
7
  baseUrl?: string;
8
- /** Optional API version. Defaults to 'v1' */
8
+ /** Optional API version. Defaults to 'v1.0' */
9
9
  version?: ApiVersion;
10
10
  /** Optional custom fetch implementation for cross-platform compatibility */
11
11
  customFetch?: CustomFetch;
12
- /** Optional headers sent with every request */
13
- headers?: Record<string, string>;
12
+ /** Optional headers sent with every request, or a function called once per request */
13
+ headers?: CustomHeaders;
14
14
  };
15
15
  /**
16
16
  * Base class for every Comms API client. Centralizes URL handling and
@@ -21,7 +21,7 @@ export declare class BaseClient {
21
21
  protected readonly baseUrl?: string;
22
22
  protected readonly defaultVersion: ApiVersion;
23
23
  protected readonly customFetch?: CustomFetch;
24
- protected readonly headers?: Record<string, string>;
24
+ protected readonly headers?: CustomHeaders;
25
25
  constructor(config: ClientConfig);
26
26
  /**
27
27
  * Returns the base URI for an API request, with a guaranteed trailing
@@ -59,7 +59,7 @@ export declare const ChannelListSchema: z.ZodArray<z.ZodPipe<z.ZodObject<{
59
59
  filters?: Record<string, string> | null | undefined;
60
60
  }>>>;
61
61
  /**
62
- * Client for `/api/v1/channels/`. The SDK auto-generates an `id` on
62
+ * Client for `/api/v1.0/channels/`. The SDK auto-generates an `id` on
63
63
  * `createChannel` when the caller doesn't supply one — pass your own `id`
64
64
  * to keep an optimistic-UI ID stable through the round-trip.
65
65
  */
@@ -130,7 +130,7 @@ export declare const CommentListSchema: z.ZodArray<z.ZodPipe<z.ZodObject<{
130
130
  actions?: unknown[] | null | undefined;
131
131
  }>>>;
132
132
  /**
133
- * Client for `/api/v1/comments/`. The SDK auto-generates the comment `id`
133
+ * Client for `/api/v1.0/comments/`. The SDK auto-generates the comment `id`
134
134
  * on `createComment` when the caller doesn't supply one.
135
135
  */
136
136
  export declare class CommentsClient extends BaseClient {
@@ -109,7 +109,7 @@ export declare const ConversationMessageListSchema: z.ZodArray<z.ZodPipe<z.ZodOb
109
109
  version?: number | null | undefined;
110
110
  }>>>;
111
111
  /**
112
- * Client for `/api/v1/conversation_messages/`. The SDK auto-generates the
112
+ * Client for `/api/v1.0/conversation_messages/`. The SDK auto-generates the
113
113
  * message `id` on `createMessage` when the caller doesn't supply one.
114
114
  */
115
115
  export declare class ConversationMessagesClient extends BaseClient {
@@ -157,7 +157,7 @@ export declare const ConversationListSchema: z.ZodArray<z.ZodPipe<z.ZodObject<{
157
157
  } | null | undefined;
158
158
  }>>>;
159
159
  /**
160
- * Client for `/api/v1/conversations/`. `getOrCreate` requires an `id` (the
160
+ * Client for `/api/v1.0/conversations/`. `getOrCreate` requires an `id` (the
161
161
  * SDK auto-generates one for new conversations); the backend dedupes on
162
162
  * `userIds`, so an existing conversation will be returned with its own
163
163
  * already-assigned `id` and your generated one is silently dropped.
@@ -11,7 +11,7 @@ export declare const GroupListSchema: z.ZodArray<z.ZodObject<{
11
11
  version: z.ZodNumber;
12
12
  }, z.core.$strip>>;
13
13
  /**
14
- * Client for `/api/v1/groups/`. The broadcast markers `EVERYONE` /
14
+ * Client for `/api/v1.0/groups/`. The broadcast markers `EVERYONE` /
15
15
  * `EVERYONE_IN_THREAD` are NOT addressable through these endpoints — they
16
16
  * only appear as members of `direct_group_mentions` / `groups` lists on
17
17
  * thread/comment writes.
@@ -2,7 +2,7 @@ import { type HookSubscribeResponse, type StatusOk } from '../types/entities.js'
2
2
  import type { SubscribeHookArgs, UnsubscribeHookArgs } from '../types/requests.js';
3
3
  import { BaseClient } from './base-client.js';
4
4
  /**
5
- * Client for `/api/v1/hooks/`. REST hooks require Todoist OAuth bearer tokens
5
+ * Client for `/api/v1.0/hooks/`. REST hooks require Todoist OAuth bearer tokens
6
6
  * that include Comms audience; session tokens are rejected by the backend.
7
7
  */
8
8
  export declare class HooksClient extends BaseClient {
@@ -380,7 +380,7 @@ export declare const InboxThreadListSchema: z.ZodArray<z.ZodPipe<z.ZodObject<{
380
380
  toEmails?: string[] | null | undefined;
381
381
  version?: number | null | undefined;
382
382
  }>>>;
383
- /** Client for `/api/v1/inbox/`. */
383
+ /** Client for `/api/v1.0/inbox/`. */
384
384
  export declare class InboxClient extends BaseClient {
385
385
  private readonly linkBaseUrl;
386
386
  private readonly inboxThreadSchema;
@@ -288,7 +288,7 @@ export declare const ThreadListSchema: z.ZodArray<z.ZodPipe<z.ZodObject<{
288
288
  } | null | undefined;
289
289
  }>>>;
290
290
  /**
291
- * Client for `/api/v1/threads/`. The SDK auto-generates the thread `id` on
291
+ * Client for `/api/v1.0/threads/`. The SDK auto-generates the thread `id` on
292
292
  * `createThread` when the caller doesn't supply one.
293
293
  */
294
294
  export declare class ThreadsClient extends BaseClient {
@@ -33,7 +33,7 @@ type MfaChallengeArgs = {
33
33
  methodType: string;
34
34
  };
35
35
  /**
36
- * Client for the `/api/v1/users/` endpoints. Authentication flows through
36
+ * Client for the `/api/v1.0/users/` endpoints. Authentication flows through
37
37
  * Todoist-ID; `register` / `login` / `loginWithGoogle` / `loginWithToken` /
38
38
  * `loginWithTodoist` are the available entry points.
39
39
  */
@@ -3,7 +3,7 @@ import { UserType } from '../types/enums.js';
3
3
  import type { GetUserByEmailArgs, GetUserByIdArgs, GetUserInfoArgs, GetUserLocalTimeArgs, GetWorkspaceUsersArgs } from '../types/requests.js';
4
4
  import { BaseClient } from './base-client.js';
5
5
  /**
6
- * Client for `/api/v1/workspace_users/`. The backend's `add` endpoint
6
+ * Client for `/api/v1.0/workspace_users/`. The backend's `add` endpoint
7
7
  * rejects non-empty `name` and `channelIds` — set neither.
8
8
  */
9
9
  export declare class WorkspaceUsersClient extends BaseClient {
@@ -58,7 +58,7 @@ export declare const ChannelListSchema: z.ZodArray<z.ZodPipe<z.ZodObject<{
58
58
  filters?: Record<string, string> | null | undefined;
59
59
  }>>>;
60
60
  /**
61
- * Client for `/api/v1/workspaces/`. Workspace IDs are integers. The backend
61
+ * Client for `/api/v1.0/workspaces/`. Workspace IDs are integers. The backend
62
62
  * currently rejects any `color` other than `1` on add/update.
63
63
  */
64
64
  export declare class WorkspacesClient extends BaseClient {
@@ -13,16 +13,21 @@ import { UsersClient } from './clients/users-client.js';
13
13
  import { WorkspaceUsersClient } from './clients/workspace-users-client.js';
14
14
  import { WorkspacesClient } from './clients/workspaces-client.js';
15
15
  import type { ApiVersion } from './types/api-version.js';
16
- import type { CustomFetch } from './types/http.js';
16
+ import type { CustomFetch, CustomHeaders } from './types/http.js';
17
17
  export type CommsApiOptions = {
18
18
  /** Optional custom API base URL. If not provided, defaults to Comms' standard API endpoint. */
19
19
  baseUrl?: string;
20
- /** Optional API version. Defaults to 'v1'. */
20
+ /** Optional API version. Defaults to 'v1.0'. */
21
21
  version?: ApiVersion;
22
22
  /** Optional custom fetch implementation for cross-platform compatibility (e.g., Obsidian, React Native, Electron). */
23
23
  customFetch?: CustomFetch;
24
- /** Optional headers sent with every request, e.g. `{ 'Doist-Platform': 'cli' }` for client attribution. Cannot override `Authorization` or `X-Request-Id`. */
25
- headers?: Record<string, string>;
24
+ /**
25
+ * Optional headers sent with every request, e.g. `{ 'Doist-Platform': 'cli' }` for client
26
+ * attribution. Pass a function instead to compute them per request, e.g. a fresh request ID;
27
+ * it is called once per request and retries reuse its result. Cannot override `Authorization`
28
+ * or `X-Request-Id`.
29
+ */
30
+ headers?: CustomHeaders;
26
31
  };
27
32
  /**
28
33
  * The main API client for interacting with the Comms REST API.
@@ -3,11 +3,11 @@ import type { ApiVersion } from '../types/api-version.js';
3
3
  * Gets the base URI for Comms API requests.
4
4
  *
5
5
  * Preserves any path component on `domainBase` so callers can route through
6
- * a proxy (e.g. `https://proxy.example.com/comms` → `.../comms/api/v1/`).
6
+ * a proxy (e.g. `https://proxy.example.com/comms` → `.../comms/api/v1.0/`).
7
7
  *
8
- * @param version - API version. Defaults to 'v1'.
8
+ * @param version - API version. Defaults to 'v1.0'.
9
9
  * @param domainBase - Custom domain base URL. Defaults to Comms' API domain.
10
- * @returns Complete base URI with trailing slash (e.g., 'https://comms.todoist.com/api/v1/')
10
+ * @returns Complete base URI with trailing slash (e.g., 'https://comms.todoist.com/api/v1.0/')
11
11
  */
12
12
  export declare function getCommsBaseUri(version?: ApiVersion, domainBase?: string): string;
13
13
  export declare const ENDPOINT_USERS = "users";
@@ -1,4 +1,4 @@
1
- import type { CustomFetch, HttpMethod, HttpResponse } from '../types/http.js';
1
+ import type { CustomFetch, CustomHeaders, HttpMethod, HttpResponse } from '../types/http.js';
2
2
  export declare function paramsSerializer(params: Record<string, unknown>): string;
3
3
  export type RequestArgs = {
4
4
  httpMethod: HttpMethod;
@@ -7,7 +7,7 @@ export type RequestArgs = {
7
7
  apiToken?: string;
8
8
  payload?: Record<string, unknown>;
9
9
  requestId?: string;
10
- headers?: Record<string, string>;
10
+ headers?: CustomHeaders;
11
11
  customFetch?: CustomFetch;
12
12
  };
13
13
  export declare function request<T>(args: RequestArgs): Promise<HttpResponse<T>>;
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Supported Comms API versions
3
3
  */
4
- export declare const API_VERSIONS: readonly ["v1"];
4
+ export declare const API_VERSIONS: readonly ["v1.0"];
5
5
  export type ApiVersion = (typeof API_VERSIONS)[number];
6
6
  export declare const DEFAULT_API_VERSION: ApiVersion;
@@ -5,6 +5,12 @@ export type HttpResponse<T = unknown> = {
5
5
  status: number;
6
6
  headers: Record<string, string>;
7
7
  };
8
+ /**
9
+ * Extra headers sent with every API request: either a fixed record, or a function
10
+ * the SDK calls once per request (retries reuse its result) for values that change
11
+ * between requests, such as a per-request ID or the command currently running.
12
+ */
13
+ export type CustomHeaders = Record<string, string> | (() => Record<string, string>);
8
14
  export type RequestConfig = {
9
15
  baseURL?: string;
10
16
  headers?: Record<string, string>;
@@ -0,0 +1,13 @@
1
+ import type { CustomHeaders } from '../types/http.js';
2
+ /**
3
+ * Merges header records left to right. HTTP header names are case-insensitive,
4
+ * so a later record replaces any earlier header with the same name whatever
5
+ * its case. Without this, a caller's `authorization` would sit alongside the
6
+ * SDK's `Authorization` and `fetch` would join the two into one value.
7
+ */
8
+ export declare function mergeHeaders(...sources: (Record<string, string> | undefined)[]): Record<string, string>;
9
+ /**
10
+ * Turns the `headers` option into the record to send with one request, calling
11
+ * it when it is a function so per-request values are fresh.
12
+ */
13
+ export declare function resolveCustomHeaders(headers: CustomHeaders | undefined): Record<string, string> | undefined;
@@ -1,4 +1,4 @@
1
- import type { CustomFetch } from '../types/http.js';
1
+ import type { CustomFetch, CustomHeaders } from '../types/http.js';
2
2
  /**
3
3
  * File content accepted by the upload helpers.
4
4
  *
@@ -12,7 +12,7 @@ import type { CustomFetch } from '../types/http.js';
12
12
  */
13
13
  export type UploadFile = Blob | Uint8Array;
14
14
  type UploadMultipartFileArgs = {
15
- /** Base API URI with trailing slash, e.g. `https://comms.todoist.com/api/v1/`. */
15
+ /** Base API URI with trailing slash, e.g. `https://comms.todoist.com/api/v1.0/`. */
16
16
  baseUrl: string;
17
17
  /** API token used for `Authorization: Bearer`. */
18
18
  authToken: string;
@@ -33,8 +33,8 @@ type UploadMultipartFileArgs = {
33
33
  maxRetries?: number;
34
34
  /** Optional request ID for tracing. */
35
35
  requestId?: string;
36
- /** Optional headers sent with the request. Cannot override `Authorization` or `Content-Type`. */
37
- headers?: Record<string, string>;
36
+ /** Optional headers sent with the request. Cannot override `Authorization`, `X-Request-Id` or `Content-Type`. */
37
+ headers?: CustomHeaders;
38
38
  /** Optional custom fetch implementation. */
39
39
  customFetch?: CustomFetch;
40
40
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@doist/comms-sdk",
3
- "version": "3.6.0",
3
+ "version": "3.8.1",
4
4
  "description": "A TypeScript wrapper for the Comms REST API.",
5
5
  "author": "Doist developers",
6
6
  "homepage": "https://doist.github.io/comms-sdk-typescript/",