diff --git a/README.md b/README.md index 154a985c..ff18058d 100644 --- a/README.md +++ b/README.md @@ -151,7 +151,9 @@ Some asynchronous operations, e.g., unlocking a door, return an [action attempt] Seam tracks the progress of requested operation and updates the action attempt. To make working with action attempts more convenient for applications, -this library provides the `waitForActionAttempt` option: +this library provides the `waitForActionAttempt` option. + +Pass the option per-request, ```ts await seam.locks.unlockDoor( @@ -162,6 +164,17 @@ await seam.locks.unlockDoor( ) ``` +or set the default option for the client: + +```ts +const seam = new SeamHttp({ + apiKey: 'your-api-key', + waitForActionAttempt: true, +}) + +await seam.locks.unlockDoor({ device_id }) +``` + Using the `waitForActionAttempt` option: - Polls the action attempt up to the `timeout` diff --git a/generate-routes.ts b/generate-routes.ts index ca0806bd..29c98048 100644 --- a/generate-routes.ts +++ b/generate-routes.ts @@ -254,11 +254,14 @@ import { type SeamHttpOptionsWithClientSessionToken, type SeamHttpOptionsWithConsoleSessionToken, type SeamHttpOptionsWithPersonalAccessToken, + type SeamHttpRequestOptions, } from 'lib/seam/connect/options.js' -import { parseOptions } from 'lib/seam/connect/parse-options.js' +import { + limitToSeamHttpRequestOptions, + parseOptions +} from 'lib/seam/connect/parse-options.js' import { resolveActionAttempt, - type ResolveActionAttemptOptions, } from 'lib/seam/connect/resolve-action-attempt.js' ${ @@ -291,6 +294,7 @@ const renderClass = ( ` export class SeamHttp${pascalCase(namespace)} { client: Client + readonly defaults: Required ${constructors .replaceAll(': SeamHttp ', `: SeamHttp${pascalCase(namespace)} `) @@ -342,10 +346,11 @@ const renderClassMethod = ({ }) ${ resource === 'action_attempt' - ? `if (waitForActionAttempt != null && waitForActionAttempt !== false) { + ? `const waitForActionAttempt = options.waitForActionAttempt ?? this.defaults.waitForActionAttempt + if (waitForActionAttempt !== false) { return resolveActionAttempt( data.${resource}, - SeamHttpActionAttempts.fromClient(this.client), + SeamHttpActionAttempts.fromClient(this.client, { ...this.defaults, waitForActionAttempt: false }), typeof waitForActionAttempt === 'boolean' ? {} : waitForActionAttempt, ) }` @@ -359,9 +364,9 @@ const renderClassMethodOptions = ({ resource, }: Pick): string => { if (resource === 'action_attempt') { - return `{ waitForActionAttempt = false }: ${renderClassMethodOptionsTypeDef( - { resource }, - )} = {},` + return `options: ${renderClassMethodOptionsTypeDef({ + resource, + })} = {},` } return '' } @@ -376,11 +381,7 @@ const renderClassMethodOptionsTypeDef = ({ resource, }: Pick): string => { if (resource === 'action_attempt') { - return ` - { - waitForActionAttempt?: boolean | Partial - } - ` + return "Pick" } return 'never' } @@ -394,7 +395,7 @@ const renderSubresourceMethod = ( )}${pascalCase(subresource)} { return SeamHttp${pascalCase(namespace)}${pascalCase( subresource, - )}.fromClient(this.client) + )}.fromClient(this.client, this.defaults) } ` diff --git a/src/lib/seam/connect/client.ts b/src/lib/seam/connect/client.ts index f03a9c36..618336b7 100644 --- a/src/lib/seam/connect/client.ts +++ b/src/lib/seam/connect/client.ts @@ -12,14 +12,11 @@ export type Client = AxiosInstance export interface ClientOptions { axiosOptions?: AxiosRequestConfig axiosRetryOptions?: AxiosRetryConfig - client?: Client } type AxiosRetryConfig = Parameters[1] export const createClient = (options: ClientOptions): AxiosInstance => { - if (options.client != null) return options.client - const client = axios.create({ paramsSerializer, ...options.axiosOptions, diff --git a/src/lib/seam/connect/options.ts b/src/lib/seam/connect/options.ts index 4b8f8d7a..35fc63e7 100644 --- a/src/lib/seam/connect/options.ts +++ b/src/lib/seam/connect/options.ts @@ -1,4 +1,6 @@ import type { Client, ClientOptions } from './client.js' +import { isSeamHttpRequestOption } from './parse-options.js' +import type { ResolveActionAttemptOptions } from './resolve-action-attempt.js' export type SeamHttpMultiWorkspaceOptions = | SeamHttpMultiWorkspaceOptionsWithClient @@ -13,16 +15,21 @@ export type SeamHttpOptions = | SeamHttpOptionsWithConsoleSessionToken | SeamHttpOptionsWithPersonalAccessToken -interface SeamHttpCommonOptions extends ClientOptions { +interface SeamHttpCommonOptions extends ClientOptions, SeamHttpRequestOptions { endpoint?: string } +export interface SeamHttpRequestOptions { + waitForActionAttempt?: boolean | ResolveActionAttemptOptions +} + export interface SeamHttpFromPublishableKeyOptions extends SeamHttpCommonOptions {} export interface SeamHttpOptionsFromEnv extends SeamHttpCommonOptions {} -export interface SeamHttpMultiWorkspaceOptionsWithClient { +export interface SeamHttpMultiWorkspaceOptionsWithClient + extends SeamHttpRequestOptions { client: Client } @@ -31,7 +38,7 @@ export const isSeamHttpMultiWorkspaceOptionsWithClient = ( ): options is SeamHttpMultiWorkspaceOptionsWithClient => isSeamHttpOptionsWithClient(options) -export interface SeamHttpOptionsWithClient { +export interface SeamHttpOptionsWithClient extends SeamHttpRequestOptions { client: Client } @@ -42,7 +49,7 @@ export const isSeamHttpOptionsWithClient = ( if (options.client == null) return false const keys = Object.keys(options).filter((k) => k !== 'client') - if (keys.length > 0) { + if (keys.filter((k) => !isSeamHttpRequestOption(k)).length > 0) { throw new SeamHttpInvalidOptionsError( `The client option cannot be used with any other option, but received: ${keys.join( ', ', diff --git a/src/lib/seam/connect/parse-options.ts b/src/lib/seam/connect/parse-options.ts index 9322e15d..fea20cd4 100644 --- a/src/lib/seam/connect/parse-options.ts +++ b/src/lib/seam/connect/parse-options.ts @@ -1,13 +1,14 @@ import version from 'lib/version.js' import { getAuthHeaders } from './auth.js' -import type { ClientOptions } from './client.js' +import type { Client, ClientOptions } from './client.js' import { isSeamHttpMultiWorkspaceOptionsWithClient, isSeamHttpOptionsWithClient, isSeamHttpOptionsWithClientSessionToken, type SeamHttpMultiWorkspaceOptions, type SeamHttpOptions, + type SeamHttpRequestOptions, } from './options.js' const defaultEndpoint = 'https://connect.getseam.com' @@ -21,15 +22,20 @@ export type Options = | SeamHttpMultiWorkspaceOptions | (SeamHttpOptions & { publishableKey?: string }) +type ParsedOptions = Required< + (ClientOptions | { client: Client }) & SeamHttpRequestOptions +> + export const parseOptions = ( apiKeyOrOptions: string | Options, -): ClientOptions => { +): ParsedOptions => { const options = getNormalizedOptions(apiKeyOrOptions) if (isSeamHttpOptionsWithClient(options)) return options if (isSeamHttpMultiWorkspaceOptionsWithClient(options)) return options return { + ...options, axiosOptions: { baseURL: options.endpoint ?? getEndpointFromEnv() ?? defaultEndpoint, withCredentials: isSeamHttpOptionsWithClientSessionToken(options), @@ -48,13 +54,22 @@ export const parseOptions = ( const getNormalizedOptions = ( apiKeyOrOptions: string | Options, -): SeamHttpOptions => { +): SeamHttpOptions & Required => { const options = typeof apiKeyOrOptions === 'string' ? { apiKey: apiKeyOrOptions } : apiKeyOrOptions - if (isSeamHttpOptionsWithClient(options)) return options + const requestOptions = { + waitForActionAttempt: options.waitForActionAttempt ?? false, + } + + if (isSeamHttpOptionsWithClient(options)) { + return { + ...options, + ...requestOptions, + } + } const apiKey = 'apiKey' in options ? options.apiKey : getApiKeyFromEnv(options) @@ -62,6 +77,7 @@ const getNormalizedOptions = ( return { ...options, ...(apiKey != null ? { apiKey } : {}), + ...requestOptions, } } @@ -80,3 +96,26 @@ const getEndpointFromEnv = (): string | null | undefined => { globalThis.process?.env?.SEAM_API_URL ) } + +export const limitToSeamHttpRequestOptions = ( + options: Required, +): Required => { + return Object.keys(options) + .filter(isSeamHttpRequestOption) + .reduce( + (obj, key) => ({ + ...obj, + [key]: options[key], + }), + {}, + ) as Required +} + +export const isSeamHttpRequestOption = ( + key: string, +): key is keyof SeamHttpRequestOptions => { + const keys: Record = { + waitForActionAttempt: true, + } + return Object.keys(keys).includes(key) +} diff --git a/src/lib/seam/connect/routes/access-codes-unmanaged.ts b/src/lib/seam/connect/routes/access-codes-unmanaged.ts index 29cf2717..7d3c8521 100644 --- a/src/lib/seam/connect/routes/access-codes-unmanaged.ts +++ b/src/lib/seam/connect/routes/access-codes-unmanaged.ts @@ -22,17 +22,23 @@ import { type SeamHttpOptionsWithClientSessionToken, type SeamHttpOptionsWithConsoleSessionToken, type SeamHttpOptionsWithPersonalAccessToken, + type SeamHttpRequestOptions, } from 'lib/seam/connect/options.js' -import { parseOptions } from 'lib/seam/connect/parse-options.js' +import { + limitToSeamHttpRequestOptions, + parseOptions, +} from 'lib/seam/connect/parse-options.js' import { SeamHttpClientSessions } from './client-sessions.js' export class SeamHttpAccessCodesUnmanaged { client: Client + readonly defaults: Required constructor(apiKeyOrOptions: string | SeamHttpOptions = {}) { - const clientOptions = parseOptions(apiKeyOrOptions) - this.client = createClient(clientOptions) + const options = parseOptions(apiKeyOrOptions) + this.client = 'client' in options ? options.client : createClient(options) + this.defaults = limitToSeamHttpRequestOptions(options) } static fromClient( @@ -78,6 +84,11 @@ export class SeamHttpAccessCodesUnmanaged { ): Promise { warnOnInsecureuserIdentifierKey(userIdentifierKey) const clientOptions = parseOptions({ ...options, publishableKey }) + if (isSeamHttpOptionsWithClient(clientOptions)) { + throw new SeamHttpInvalidOptionsError( + 'The client option cannot be used with SeamHttp.fromPublishableKey', + ) + } const client = createClient(clientOptions) const clientSessions = SeamHttpClientSessions.fromClient(client) const { token } = await clientSessions.getOrCreate({ diff --git a/src/lib/seam/connect/routes/access-codes.ts b/src/lib/seam/connect/routes/access-codes.ts index c9600c29..757d5d5b 100644 --- a/src/lib/seam/connect/routes/access-codes.ts +++ b/src/lib/seam/connect/routes/access-codes.ts @@ -22,18 +22,24 @@ import { type SeamHttpOptionsWithClientSessionToken, type SeamHttpOptionsWithConsoleSessionToken, type SeamHttpOptionsWithPersonalAccessToken, + type SeamHttpRequestOptions, } from 'lib/seam/connect/options.js' -import { parseOptions } from 'lib/seam/connect/parse-options.js' +import { + limitToSeamHttpRequestOptions, + parseOptions, +} from 'lib/seam/connect/parse-options.js' import { SeamHttpAccessCodesUnmanaged } from './access-codes-unmanaged.js' import { SeamHttpClientSessions } from './client-sessions.js' export class SeamHttpAccessCodes { client: Client + readonly defaults: Required constructor(apiKeyOrOptions: string | SeamHttpOptions = {}) { - const clientOptions = parseOptions(apiKeyOrOptions) - this.client = createClient(clientOptions) + const options = parseOptions(apiKeyOrOptions) + this.client = 'client' in options ? options.client : createClient(options) + this.defaults = limitToSeamHttpRequestOptions(options) } static fromClient( @@ -79,6 +85,11 @@ export class SeamHttpAccessCodes { ): Promise { warnOnInsecureuserIdentifierKey(userIdentifierKey) const clientOptions = parseOptions({ ...options, publishableKey }) + if (isSeamHttpOptionsWithClient(clientOptions)) { + throw new SeamHttpInvalidOptionsError( + 'The client option cannot be used with SeamHttp.fromPublishableKey', + ) + } const client = createClient(clientOptions) const clientSessions = SeamHttpClientSessions.fromClient(client) const { token } = await clientSessions.getOrCreate({ @@ -122,7 +133,7 @@ export class SeamHttpAccessCodes { } get unmanaged(): SeamHttpAccessCodesUnmanaged { - return SeamHttpAccessCodesUnmanaged.fromClient(this.client) + return SeamHttpAccessCodesUnmanaged.fromClient(this.client, this.defaults) } async create( diff --git a/src/lib/seam/connect/routes/acs-access-groups.ts b/src/lib/seam/connect/routes/acs-access-groups.ts index c45f8985..8829480e 100644 --- a/src/lib/seam/connect/routes/acs-access-groups.ts +++ b/src/lib/seam/connect/routes/acs-access-groups.ts @@ -22,17 +22,23 @@ import { type SeamHttpOptionsWithClientSessionToken, type SeamHttpOptionsWithConsoleSessionToken, type SeamHttpOptionsWithPersonalAccessToken, + type SeamHttpRequestOptions, } from 'lib/seam/connect/options.js' -import { parseOptions } from 'lib/seam/connect/parse-options.js' +import { + limitToSeamHttpRequestOptions, + parseOptions, +} from 'lib/seam/connect/parse-options.js' import { SeamHttpClientSessions } from './client-sessions.js' export class SeamHttpAcsAccessGroups { client: Client + readonly defaults: Required constructor(apiKeyOrOptions: string | SeamHttpOptions = {}) { - const clientOptions = parseOptions(apiKeyOrOptions) - this.client = createClient(clientOptions) + const options = parseOptions(apiKeyOrOptions) + this.client = 'client' in options ? options.client : createClient(options) + this.defaults = limitToSeamHttpRequestOptions(options) } static fromClient( @@ -78,6 +84,11 @@ export class SeamHttpAcsAccessGroups { ): Promise { warnOnInsecureuserIdentifierKey(userIdentifierKey) const clientOptions = parseOptions({ ...options, publishableKey }) + if (isSeamHttpOptionsWithClient(clientOptions)) { + throw new SeamHttpInvalidOptionsError( + 'The client option cannot be used with SeamHttp.fromPublishableKey', + ) + } const client = createClient(clientOptions) const clientSessions = SeamHttpClientSessions.fromClient(client) const { token } = await clientSessions.getOrCreate({ diff --git a/src/lib/seam/connect/routes/acs-credentials.ts b/src/lib/seam/connect/routes/acs-credentials.ts index 27f952e7..d49ab14f 100644 --- a/src/lib/seam/connect/routes/acs-credentials.ts +++ b/src/lib/seam/connect/routes/acs-credentials.ts @@ -22,17 +22,23 @@ import { type SeamHttpOptionsWithClientSessionToken, type SeamHttpOptionsWithConsoleSessionToken, type SeamHttpOptionsWithPersonalAccessToken, + type SeamHttpRequestOptions, } from 'lib/seam/connect/options.js' -import { parseOptions } from 'lib/seam/connect/parse-options.js' +import { + limitToSeamHttpRequestOptions, + parseOptions, +} from 'lib/seam/connect/parse-options.js' import { SeamHttpClientSessions } from './client-sessions.js' export class SeamHttpAcsCredentials { client: Client + readonly defaults: Required constructor(apiKeyOrOptions: string | SeamHttpOptions = {}) { - const clientOptions = parseOptions(apiKeyOrOptions) - this.client = createClient(clientOptions) + const options = parseOptions(apiKeyOrOptions) + this.client = 'client' in options ? options.client : createClient(options) + this.defaults = limitToSeamHttpRequestOptions(options) } static fromClient( @@ -78,6 +84,11 @@ export class SeamHttpAcsCredentials { ): Promise { warnOnInsecureuserIdentifierKey(userIdentifierKey) const clientOptions = parseOptions({ ...options, publishableKey }) + if (isSeamHttpOptionsWithClient(clientOptions)) { + throw new SeamHttpInvalidOptionsError( + 'The client option cannot be used with SeamHttp.fromPublishableKey', + ) + } const client = createClient(clientOptions) const clientSessions = SeamHttpClientSessions.fromClient(client) const { token } = await clientSessions.getOrCreate({ diff --git a/src/lib/seam/connect/routes/acs-systems.ts b/src/lib/seam/connect/routes/acs-systems.ts index be5e714d..9e8a83d3 100644 --- a/src/lib/seam/connect/routes/acs-systems.ts +++ b/src/lib/seam/connect/routes/acs-systems.ts @@ -22,17 +22,23 @@ import { type SeamHttpOptionsWithClientSessionToken, type SeamHttpOptionsWithConsoleSessionToken, type SeamHttpOptionsWithPersonalAccessToken, + type SeamHttpRequestOptions, } from 'lib/seam/connect/options.js' -import { parseOptions } from 'lib/seam/connect/parse-options.js' +import { + limitToSeamHttpRequestOptions, + parseOptions, +} from 'lib/seam/connect/parse-options.js' import { SeamHttpClientSessions } from './client-sessions.js' export class SeamHttpAcsSystems { client: Client + readonly defaults: Required constructor(apiKeyOrOptions: string | SeamHttpOptions = {}) { - const clientOptions = parseOptions(apiKeyOrOptions) - this.client = createClient(clientOptions) + const options = parseOptions(apiKeyOrOptions) + this.client = 'client' in options ? options.client : createClient(options) + this.defaults = limitToSeamHttpRequestOptions(options) } static fromClient( @@ -78,6 +84,11 @@ export class SeamHttpAcsSystems { ): Promise { warnOnInsecureuserIdentifierKey(userIdentifierKey) const clientOptions = parseOptions({ ...options, publishableKey }) + if (isSeamHttpOptionsWithClient(clientOptions)) { + throw new SeamHttpInvalidOptionsError( + 'The client option cannot be used with SeamHttp.fromPublishableKey', + ) + } const client = createClient(clientOptions) const clientSessions = SeamHttpClientSessions.fromClient(client) const { token } = await clientSessions.getOrCreate({ diff --git a/src/lib/seam/connect/routes/acs-users.ts b/src/lib/seam/connect/routes/acs-users.ts index 3f7626d6..beeb2c0d 100644 --- a/src/lib/seam/connect/routes/acs-users.ts +++ b/src/lib/seam/connect/routes/acs-users.ts @@ -22,17 +22,23 @@ import { type SeamHttpOptionsWithClientSessionToken, type SeamHttpOptionsWithConsoleSessionToken, type SeamHttpOptionsWithPersonalAccessToken, + type SeamHttpRequestOptions, } from 'lib/seam/connect/options.js' -import { parseOptions } from 'lib/seam/connect/parse-options.js' +import { + limitToSeamHttpRequestOptions, + parseOptions, +} from 'lib/seam/connect/parse-options.js' import { SeamHttpClientSessions } from './client-sessions.js' export class SeamHttpAcsUsers { client: Client + readonly defaults: Required constructor(apiKeyOrOptions: string | SeamHttpOptions = {}) { - const clientOptions = parseOptions(apiKeyOrOptions) - this.client = createClient(clientOptions) + const options = parseOptions(apiKeyOrOptions) + this.client = 'client' in options ? options.client : createClient(options) + this.defaults = limitToSeamHttpRequestOptions(options) } static fromClient( @@ -78,6 +84,11 @@ export class SeamHttpAcsUsers { ): Promise { warnOnInsecureuserIdentifierKey(userIdentifierKey) const clientOptions = parseOptions({ ...options, publishableKey }) + if (isSeamHttpOptionsWithClient(clientOptions)) { + throw new SeamHttpInvalidOptionsError( + 'The client option cannot be used with SeamHttp.fromPublishableKey', + ) + } const client = createClient(clientOptions) const clientSessions = SeamHttpClientSessions.fromClient(client) const { token } = await clientSessions.getOrCreate({ diff --git a/src/lib/seam/connect/routes/acs.ts b/src/lib/seam/connect/routes/acs.ts index b5e13aa1..cdfceb14 100644 --- a/src/lib/seam/connect/routes/acs.ts +++ b/src/lib/seam/connect/routes/acs.ts @@ -19,8 +19,12 @@ import { type SeamHttpOptionsWithClientSessionToken, type SeamHttpOptionsWithConsoleSessionToken, type SeamHttpOptionsWithPersonalAccessToken, + type SeamHttpRequestOptions, } from 'lib/seam/connect/options.js' -import { parseOptions } from 'lib/seam/connect/parse-options.js' +import { + limitToSeamHttpRequestOptions, + parseOptions, +} from 'lib/seam/connect/parse-options.js' import { SeamHttpAcsAccessGroups } from './acs-access-groups.js' import { SeamHttpAcsCredentials } from './acs-credentials.js' @@ -30,10 +34,12 @@ import { SeamHttpClientSessions } from './client-sessions.js' export class SeamHttpAcs { client: Client + readonly defaults: Required constructor(apiKeyOrOptions: string | SeamHttpOptions = {}) { - const clientOptions = parseOptions(apiKeyOrOptions) - this.client = createClient(clientOptions) + const options = parseOptions(apiKeyOrOptions) + this.client = 'client' in options ? options.client : createClient(options) + this.defaults = limitToSeamHttpRequestOptions(options) } static fromClient( @@ -79,6 +85,11 @@ export class SeamHttpAcs { ): Promise { warnOnInsecureuserIdentifierKey(userIdentifierKey) const clientOptions = parseOptions({ ...options, publishableKey }) + if (isSeamHttpOptionsWithClient(clientOptions)) { + throw new SeamHttpInvalidOptionsError( + 'The client option cannot be used with SeamHttp.fromPublishableKey', + ) + } const client = createClient(clientOptions) const clientSessions = SeamHttpClientSessions.fromClient(client) const { token } = await clientSessions.getOrCreate({ @@ -122,18 +133,18 @@ export class SeamHttpAcs { } get accessGroups(): SeamHttpAcsAccessGroups { - return SeamHttpAcsAccessGroups.fromClient(this.client) + return SeamHttpAcsAccessGroups.fromClient(this.client, this.defaults) } get credentials(): SeamHttpAcsCredentials { - return SeamHttpAcsCredentials.fromClient(this.client) + return SeamHttpAcsCredentials.fromClient(this.client, this.defaults) } get systems(): SeamHttpAcsSystems { - return SeamHttpAcsSystems.fromClient(this.client) + return SeamHttpAcsSystems.fromClient(this.client, this.defaults) } get users(): SeamHttpAcsUsers { - return SeamHttpAcsUsers.fromClient(this.client) + return SeamHttpAcsUsers.fromClient(this.client, this.defaults) } } diff --git a/src/lib/seam/connect/routes/action-attempts.ts b/src/lib/seam/connect/routes/action-attempts.ts index fe20dffa..585fb674 100644 --- a/src/lib/seam/connect/routes/action-attempts.ts +++ b/src/lib/seam/connect/routes/action-attempts.ts @@ -22,21 +22,24 @@ import { type SeamHttpOptionsWithClientSessionToken, type SeamHttpOptionsWithConsoleSessionToken, type SeamHttpOptionsWithPersonalAccessToken, + type SeamHttpRequestOptions, } from 'lib/seam/connect/options.js' -import { parseOptions } from 'lib/seam/connect/parse-options.js' import { - resolveActionAttempt, - type ResolveActionAttemptOptions, -} from 'lib/seam/connect/resolve-action-attempt.js' + limitToSeamHttpRequestOptions, + parseOptions, +} from 'lib/seam/connect/parse-options.js' +import { resolveActionAttempt } from 'lib/seam/connect/resolve-action-attempt.js' import { SeamHttpClientSessions } from './client-sessions.js' export class SeamHttpActionAttempts { client: Client + readonly defaults: Required constructor(apiKeyOrOptions: string | SeamHttpOptions = {}) { - const clientOptions = parseOptions(apiKeyOrOptions) - this.client = createClient(clientOptions) + const options = parseOptions(apiKeyOrOptions) + this.client = 'client' in options ? options.client : createClient(options) + this.defaults = limitToSeamHttpRequestOptions(options) } static fromClient( @@ -82,6 +85,11 @@ export class SeamHttpActionAttempts { ): Promise { warnOnInsecureuserIdentifierKey(userIdentifierKey) const clientOptions = parseOptions({ ...options, publishableKey }) + if (isSeamHttpOptionsWithClient(clientOptions)) { + throw new SeamHttpInvalidOptionsError( + 'The client option cannot be used with SeamHttp.fromPublishableKey', + ) + } const client = createClient(clientOptions) const clientSessions = SeamHttpClientSessions.fromClient(client) const { token } = await clientSessions.getOrCreate({ @@ -126,21 +134,22 @@ export class SeamHttpActionAttempts { async get( body?: ActionAttemptsGetParams, - { - waitForActionAttempt = false, - }: { - waitForActionAttempt?: boolean | Partial - } = {}, + options: Pick = {}, ): Promise { const { data } = await this.client.request({ url: '/action_attempts/get', method: 'post', data: body, }) - if (waitForActionAttempt != null && waitForActionAttempt !== false) { + const waitForActionAttempt = + options.waitForActionAttempt ?? this.defaults.waitForActionAttempt + if (waitForActionAttempt !== false) { return await resolveActionAttempt( data.action_attempt, - SeamHttpActionAttempts.fromClient(this.client), + SeamHttpActionAttempts.fromClient(this.client, { + ...this.defaults, + waitForActionAttempt: false, + }), typeof waitForActionAttempt === 'boolean' ? {} : waitForActionAttempt, ) } @@ -166,9 +175,10 @@ export type ActionAttemptsGetResponse = SetNonNullable< Required> > -export interface ActionAttemptsGetOptions { - waitForActionAttempt?: boolean | Partial -} +export type ActionAttemptsGetOptions = Pick< + SeamHttpRequestOptions, + 'waitForActionAttempt' +> export type ActionAttemptsListParams = RouteRequestBody<'/action_attempts/list'> diff --git a/src/lib/seam/connect/routes/client-sessions.ts b/src/lib/seam/connect/routes/client-sessions.ts index 1d0f0117..824bbce1 100644 --- a/src/lib/seam/connect/routes/client-sessions.ts +++ b/src/lib/seam/connect/routes/client-sessions.ts @@ -22,15 +22,21 @@ import { type SeamHttpOptionsWithClientSessionToken, type SeamHttpOptionsWithConsoleSessionToken, type SeamHttpOptionsWithPersonalAccessToken, + type SeamHttpRequestOptions, } from 'lib/seam/connect/options.js' -import { parseOptions } from 'lib/seam/connect/parse-options.js' +import { + limitToSeamHttpRequestOptions, + parseOptions, +} from 'lib/seam/connect/parse-options.js' export class SeamHttpClientSessions { client: Client + readonly defaults: Required constructor(apiKeyOrOptions: string | SeamHttpOptions = {}) { - const clientOptions = parseOptions(apiKeyOrOptions) - this.client = createClient(clientOptions) + const options = parseOptions(apiKeyOrOptions) + this.client = 'client' in options ? options.client : createClient(options) + this.defaults = limitToSeamHttpRequestOptions(options) } static fromClient( @@ -76,6 +82,11 @@ export class SeamHttpClientSessions { ): Promise { warnOnInsecureuserIdentifierKey(userIdentifierKey) const clientOptions = parseOptions({ ...options, publishableKey }) + if (isSeamHttpOptionsWithClient(clientOptions)) { + throw new SeamHttpInvalidOptionsError( + 'The client option cannot be used with SeamHttp.fromPublishableKey', + ) + } const client = createClient(clientOptions) const clientSessions = SeamHttpClientSessions.fromClient(client) const { token } = await clientSessions.getOrCreate({ diff --git a/src/lib/seam/connect/routes/connect-webviews.ts b/src/lib/seam/connect/routes/connect-webviews.ts index 52a62506..f98678a0 100644 --- a/src/lib/seam/connect/routes/connect-webviews.ts +++ b/src/lib/seam/connect/routes/connect-webviews.ts @@ -26,17 +26,23 @@ import { type SeamHttpOptionsWithClientSessionToken, type SeamHttpOptionsWithConsoleSessionToken, type SeamHttpOptionsWithPersonalAccessToken, + type SeamHttpRequestOptions, } from 'lib/seam/connect/options.js' -import { parseOptions } from 'lib/seam/connect/parse-options.js' +import { + limitToSeamHttpRequestOptions, + parseOptions, +} from 'lib/seam/connect/parse-options.js' import { SeamHttpClientSessions } from './client-sessions.js' export class SeamHttpConnectWebviews { client: Client + readonly defaults: Required constructor(apiKeyOrOptions: string | SeamHttpOptions = {}) { - const clientOptions = parseOptions(apiKeyOrOptions) - this.client = createClient(clientOptions) + const options = parseOptions(apiKeyOrOptions) + this.client = 'client' in options ? options.client : createClient(options) + this.defaults = limitToSeamHttpRequestOptions(options) } static fromClient( @@ -82,6 +88,11 @@ export class SeamHttpConnectWebviews { ): Promise { warnOnInsecureuserIdentifierKey(userIdentifierKey) const clientOptions = parseOptions({ ...options, publishableKey }) + if (isSeamHttpOptionsWithClient(clientOptions)) { + throw new SeamHttpInvalidOptionsError( + 'The client option cannot be used with SeamHttp.fromPublishableKey', + ) + } const client = createClient(clientOptions) const clientSessions = SeamHttpClientSessions.fromClient(client) const { token } = await clientSessions.getOrCreate({ diff --git a/src/lib/seam/connect/routes/connected-accounts.ts b/src/lib/seam/connect/routes/connected-accounts.ts index 980ca370..fff6c7ac 100644 --- a/src/lib/seam/connect/routes/connected-accounts.ts +++ b/src/lib/seam/connect/routes/connected-accounts.ts @@ -26,17 +26,23 @@ import { type SeamHttpOptionsWithClientSessionToken, type SeamHttpOptionsWithConsoleSessionToken, type SeamHttpOptionsWithPersonalAccessToken, + type SeamHttpRequestOptions, } from 'lib/seam/connect/options.js' -import { parseOptions } from 'lib/seam/connect/parse-options.js' +import { + limitToSeamHttpRequestOptions, + parseOptions, +} from 'lib/seam/connect/parse-options.js' import { SeamHttpClientSessions } from './client-sessions.js' export class SeamHttpConnectedAccounts { client: Client + readonly defaults: Required constructor(apiKeyOrOptions: string | SeamHttpOptions = {}) { - const clientOptions = parseOptions(apiKeyOrOptions) - this.client = createClient(clientOptions) + const options = parseOptions(apiKeyOrOptions) + this.client = 'client' in options ? options.client : createClient(options) + this.defaults = limitToSeamHttpRequestOptions(options) } static fromClient( @@ -82,6 +88,11 @@ export class SeamHttpConnectedAccounts { ): Promise { warnOnInsecureuserIdentifierKey(userIdentifierKey) const clientOptions = parseOptions({ ...options, publishableKey }) + if (isSeamHttpOptionsWithClient(clientOptions)) { + throw new SeamHttpInvalidOptionsError( + 'The client option cannot be used with SeamHttp.fromPublishableKey', + ) + } const client = createClient(clientOptions) const clientSessions = SeamHttpClientSessions.fromClient(client) const { token } = await clientSessions.getOrCreate({ diff --git a/src/lib/seam/connect/routes/devices-unmanaged.ts b/src/lib/seam/connect/routes/devices-unmanaged.ts index df5d06c9..d25bc53d 100644 --- a/src/lib/seam/connect/routes/devices-unmanaged.ts +++ b/src/lib/seam/connect/routes/devices-unmanaged.ts @@ -22,17 +22,23 @@ import { type SeamHttpOptionsWithClientSessionToken, type SeamHttpOptionsWithConsoleSessionToken, type SeamHttpOptionsWithPersonalAccessToken, + type SeamHttpRequestOptions, } from 'lib/seam/connect/options.js' -import { parseOptions } from 'lib/seam/connect/parse-options.js' +import { + limitToSeamHttpRequestOptions, + parseOptions, +} from 'lib/seam/connect/parse-options.js' import { SeamHttpClientSessions } from './client-sessions.js' export class SeamHttpDevicesUnmanaged { client: Client + readonly defaults: Required constructor(apiKeyOrOptions: string | SeamHttpOptions = {}) { - const clientOptions = parseOptions(apiKeyOrOptions) - this.client = createClient(clientOptions) + const options = parseOptions(apiKeyOrOptions) + this.client = 'client' in options ? options.client : createClient(options) + this.defaults = limitToSeamHttpRequestOptions(options) } static fromClient( @@ -78,6 +84,11 @@ export class SeamHttpDevicesUnmanaged { ): Promise { warnOnInsecureuserIdentifierKey(userIdentifierKey) const clientOptions = parseOptions({ ...options, publishableKey }) + if (isSeamHttpOptionsWithClient(clientOptions)) { + throw new SeamHttpInvalidOptionsError( + 'The client option cannot be used with SeamHttp.fromPublishableKey', + ) + } const client = createClient(clientOptions) const clientSessions = SeamHttpClientSessions.fromClient(client) const { token } = await clientSessions.getOrCreate({ diff --git a/src/lib/seam/connect/routes/devices.ts b/src/lib/seam/connect/routes/devices.ts index 6e5fd6fa..b2eff086 100644 --- a/src/lib/seam/connect/routes/devices.ts +++ b/src/lib/seam/connect/routes/devices.ts @@ -22,18 +22,24 @@ import { type SeamHttpOptionsWithClientSessionToken, type SeamHttpOptionsWithConsoleSessionToken, type SeamHttpOptionsWithPersonalAccessToken, + type SeamHttpRequestOptions, } from 'lib/seam/connect/options.js' -import { parseOptions } from 'lib/seam/connect/parse-options.js' +import { + limitToSeamHttpRequestOptions, + parseOptions, +} from 'lib/seam/connect/parse-options.js' import { SeamHttpClientSessions } from './client-sessions.js' import { SeamHttpDevicesUnmanaged } from './devices-unmanaged.js' export class SeamHttpDevices { client: Client + readonly defaults: Required constructor(apiKeyOrOptions: string | SeamHttpOptions = {}) { - const clientOptions = parseOptions(apiKeyOrOptions) - this.client = createClient(clientOptions) + const options = parseOptions(apiKeyOrOptions) + this.client = 'client' in options ? options.client : createClient(options) + this.defaults = limitToSeamHttpRequestOptions(options) } static fromClient( @@ -79,6 +85,11 @@ export class SeamHttpDevices { ): Promise { warnOnInsecureuserIdentifierKey(userIdentifierKey) const clientOptions = parseOptions({ ...options, publishableKey }) + if (isSeamHttpOptionsWithClient(clientOptions)) { + throw new SeamHttpInvalidOptionsError( + 'The client option cannot be used with SeamHttp.fromPublishableKey', + ) + } const client = createClient(clientOptions) const clientSessions = SeamHttpClientSessions.fromClient(client) const { token } = await clientSessions.getOrCreate({ @@ -122,7 +133,7 @@ export class SeamHttpDevices { } get unmanaged(): SeamHttpDevicesUnmanaged { - return SeamHttpDevicesUnmanaged.fromClient(this.client) + return SeamHttpDevicesUnmanaged.fromClient(this.client, this.defaults) } async delete(body?: DevicesDeleteBody): Promise { diff --git a/src/lib/seam/connect/routes/events.ts b/src/lib/seam/connect/routes/events.ts index 7a3192f2..89684634 100644 --- a/src/lib/seam/connect/routes/events.ts +++ b/src/lib/seam/connect/routes/events.ts @@ -22,17 +22,23 @@ import { type SeamHttpOptionsWithClientSessionToken, type SeamHttpOptionsWithConsoleSessionToken, type SeamHttpOptionsWithPersonalAccessToken, + type SeamHttpRequestOptions, } from 'lib/seam/connect/options.js' -import { parseOptions } from 'lib/seam/connect/parse-options.js' +import { + limitToSeamHttpRequestOptions, + parseOptions, +} from 'lib/seam/connect/parse-options.js' import { SeamHttpClientSessions } from './client-sessions.js' export class SeamHttpEvents { client: Client + readonly defaults: Required constructor(apiKeyOrOptions: string | SeamHttpOptions = {}) { - const clientOptions = parseOptions(apiKeyOrOptions) - this.client = createClient(clientOptions) + const options = parseOptions(apiKeyOrOptions) + this.client = 'client' in options ? options.client : createClient(options) + this.defaults = limitToSeamHttpRequestOptions(options) } static fromClient( @@ -78,6 +84,11 @@ export class SeamHttpEvents { ): Promise { warnOnInsecureuserIdentifierKey(userIdentifierKey) const clientOptions = parseOptions({ ...options, publishableKey }) + if (isSeamHttpOptionsWithClient(clientOptions)) { + throw new SeamHttpInvalidOptionsError( + 'The client option cannot be used with SeamHttp.fromPublishableKey', + ) + } const client = createClient(clientOptions) const clientSessions = SeamHttpClientSessions.fromClient(client) const { token } = await clientSessions.getOrCreate({ diff --git a/src/lib/seam/connect/routes/locks.ts b/src/lib/seam/connect/routes/locks.ts index 05345480..5dfd7906 100644 --- a/src/lib/seam/connect/routes/locks.ts +++ b/src/lib/seam/connect/routes/locks.ts @@ -22,22 +22,25 @@ import { type SeamHttpOptionsWithClientSessionToken, type SeamHttpOptionsWithConsoleSessionToken, type SeamHttpOptionsWithPersonalAccessToken, + type SeamHttpRequestOptions, } from 'lib/seam/connect/options.js' -import { parseOptions } from 'lib/seam/connect/parse-options.js' import { - resolveActionAttempt, - type ResolveActionAttemptOptions, -} from 'lib/seam/connect/resolve-action-attempt.js' + limitToSeamHttpRequestOptions, + parseOptions, +} from 'lib/seam/connect/parse-options.js' +import { resolveActionAttempt } from 'lib/seam/connect/resolve-action-attempt.js' import { SeamHttpActionAttempts } from './action-attempts.js' import { SeamHttpClientSessions } from './client-sessions.js' export class SeamHttpLocks { client: Client + readonly defaults: Required constructor(apiKeyOrOptions: string | SeamHttpOptions = {}) { - const clientOptions = parseOptions(apiKeyOrOptions) - this.client = createClient(clientOptions) + const options = parseOptions(apiKeyOrOptions) + this.client = 'client' in options ? options.client : createClient(options) + this.defaults = limitToSeamHttpRequestOptions(options) } static fromClient( @@ -83,6 +86,11 @@ export class SeamHttpLocks { ): Promise { warnOnInsecureuserIdentifierKey(userIdentifierKey) const clientOptions = parseOptions({ ...options, publishableKey }) + if (isSeamHttpOptionsWithClient(clientOptions)) { + throw new SeamHttpInvalidOptionsError( + 'The client option cannot be used with SeamHttp.fromPublishableKey', + ) + } const client = createClient(clientOptions) const clientSessions = SeamHttpClientSessions.fromClient(client) const { token } = await clientSessions.getOrCreate({ @@ -147,21 +155,22 @@ export class SeamHttpLocks { async lockDoor( body?: LocksLockDoorBody, - { - waitForActionAttempt = false, - }: { - waitForActionAttempt?: boolean | Partial - } = {}, + options: Pick = {}, ): Promise { const { data } = await this.client.request({ url: '/locks/lock_door', method: 'post', data: body, }) - if (waitForActionAttempt != null && waitForActionAttempt !== false) { + const waitForActionAttempt = + options.waitForActionAttempt ?? this.defaults.waitForActionAttempt + if (waitForActionAttempt !== false) { return await resolveActionAttempt( data.action_attempt, - SeamHttpActionAttempts.fromClient(this.client), + SeamHttpActionAttempts.fromClient(this.client, { + ...this.defaults, + waitForActionAttempt: false, + }), typeof waitForActionAttempt === 'boolean' ? {} : waitForActionAttempt, ) } @@ -170,21 +179,22 @@ export class SeamHttpLocks { async unlockDoor( body?: LocksUnlockDoorBody, - { - waitForActionAttempt = false, - }: { - waitForActionAttempt?: boolean | Partial - } = {}, + options: Pick = {}, ): Promise { const { data } = await this.client.request({ url: '/locks/unlock_door', method: 'post', data: body, }) - if (waitForActionAttempt != null && waitForActionAttempt !== false) { + const waitForActionAttempt = + options.waitForActionAttempt ?? this.defaults.waitForActionAttempt + if (waitForActionAttempt !== false) { return await resolveActionAttempt( data.action_attempt, - SeamHttpActionAttempts.fromClient(this.client), + SeamHttpActionAttempts.fromClient(this.client, { + ...this.defaults, + waitForActionAttempt: false, + }), typeof waitForActionAttempt === 'boolean' ? {} : waitForActionAttempt, ) } @@ -214,9 +224,10 @@ export type LocksLockDoorResponse = SetNonNullable< Required> > -export interface LocksLockDoorOptions { - waitForActionAttempt?: boolean | Partial -} +export type LocksLockDoorOptions = Pick< + SeamHttpRequestOptions, + 'waitForActionAttempt' +> export type LocksUnlockDoorBody = RouteRequestBody<'/locks/unlock_door'> @@ -224,6 +235,7 @@ export type LocksUnlockDoorResponse = SetNonNullable< Required> > -export interface LocksUnlockDoorOptions { - waitForActionAttempt?: boolean | Partial -} +export type LocksUnlockDoorOptions = Pick< + SeamHttpRequestOptions, + 'waitForActionAttempt' +> diff --git a/src/lib/seam/connect/routes/noise-sensors-noise-thresholds.ts b/src/lib/seam/connect/routes/noise-sensors-noise-thresholds.ts index 27b6956b..6077bfdd 100644 --- a/src/lib/seam/connect/routes/noise-sensors-noise-thresholds.ts +++ b/src/lib/seam/connect/routes/noise-sensors-noise-thresholds.ts @@ -22,17 +22,23 @@ import { type SeamHttpOptionsWithClientSessionToken, type SeamHttpOptionsWithConsoleSessionToken, type SeamHttpOptionsWithPersonalAccessToken, + type SeamHttpRequestOptions, } from 'lib/seam/connect/options.js' -import { parseOptions } from 'lib/seam/connect/parse-options.js' +import { + limitToSeamHttpRequestOptions, + parseOptions, +} from 'lib/seam/connect/parse-options.js' import { SeamHttpClientSessions } from './client-sessions.js' export class SeamHttpNoiseSensorsNoiseThresholds { client: Client + readonly defaults: Required constructor(apiKeyOrOptions: string | SeamHttpOptions = {}) { - const clientOptions = parseOptions(apiKeyOrOptions) - this.client = createClient(clientOptions) + const options = parseOptions(apiKeyOrOptions) + this.client = 'client' in options ? options.client : createClient(options) + this.defaults = limitToSeamHttpRequestOptions(options) } static fromClient( @@ -78,6 +84,11 @@ export class SeamHttpNoiseSensorsNoiseThresholds { ): Promise { warnOnInsecureuserIdentifierKey(userIdentifierKey) const clientOptions = parseOptions({ ...options, publishableKey }) + if (isSeamHttpOptionsWithClient(clientOptions)) { + throw new SeamHttpInvalidOptionsError( + 'The client option cannot be used with SeamHttp.fromPublishableKey', + ) + } const client = createClient(clientOptions) const clientSessions = SeamHttpClientSessions.fromClient(client) const { token } = await clientSessions.getOrCreate({ diff --git a/src/lib/seam/connect/routes/noise-sensors.ts b/src/lib/seam/connect/routes/noise-sensors.ts index 17cacedb..ccbda2aa 100644 --- a/src/lib/seam/connect/routes/noise-sensors.ts +++ b/src/lib/seam/connect/routes/noise-sensors.ts @@ -19,18 +19,24 @@ import { type SeamHttpOptionsWithClientSessionToken, type SeamHttpOptionsWithConsoleSessionToken, type SeamHttpOptionsWithPersonalAccessToken, + type SeamHttpRequestOptions, } from 'lib/seam/connect/options.js' -import { parseOptions } from 'lib/seam/connect/parse-options.js' +import { + limitToSeamHttpRequestOptions, + parseOptions, +} from 'lib/seam/connect/parse-options.js' import { SeamHttpClientSessions } from './client-sessions.js' import { SeamHttpNoiseSensorsNoiseThresholds } from './noise-sensors-noise-thresholds.js' export class SeamHttpNoiseSensors { client: Client + readonly defaults: Required constructor(apiKeyOrOptions: string | SeamHttpOptions = {}) { - const clientOptions = parseOptions(apiKeyOrOptions) - this.client = createClient(clientOptions) + const options = parseOptions(apiKeyOrOptions) + this.client = 'client' in options ? options.client : createClient(options) + this.defaults = limitToSeamHttpRequestOptions(options) } static fromClient( @@ -76,6 +82,11 @@ export class SeamHttpNoiseSensors { ): Promise { warnOnInsecureuserIdentifierKey(userIdentifierKey) const clientOptions = parseOptions({ ...options, publishableKey }) + if (isSeamHttpOptionsWithClient(clientOptions)) { + throw new SeamHttpInvalidOptionsError( + 'The client option cannot be used with SeamHttp.fromPublishableKey', + ) + } const client = createClient(clientOptions) const clientSessions = SeamHttpClientSessions.fromClient(client) const { token } = await clientSessions.getOrCreate({ @@ -119,6 +130,9 @@ export class SeamHttpNoiseSensors { } get noiseThresholds(): SeamHttpNoiseSensorsNoiseThresholds { - return SeamHttpNoiseSensorsNoiseThresholds.fromClient(this.client) + return SeamHttpNoiseSensorsNoiseThresholds.fromClient( + this.client, + this.defaults, + ) } } diff --git a/src/lib/seam/connect/routes/thermostats-climate-setting-schedules.ts b/src/lib/seam/connect/routes/thermostats-climate-setting-schedules.ts index 05f5fd85..a5a2b9db 100644 --- a/src/lib/seam/connect/routes/thermostats-climate-setting-schedules.ts +++ b/src/lib/seam/connect/routes/thermostats-climate-setting-schedules.ts @@ -22,17 +22,23 @@ import { type SeamHttpOptionsWithClientSessionToken, type SeamHttpOptionsWithConsoleSessionToken, type SeamHttpOptionsWithPersonalAccessToken, + type SeamHttpRequestOptions, } from 'lib/seam/connect/options.js' -import { parseOptions } from 'lib/seam/connect/parse-options.js' +import { + limitToSeamHttpRequestOptions, + parseOptions, +} from 'lib/seam/connect/parse-options.js' import { SeamHttpClientSessions } from './client-sessions.js' export class SeamHttpThermostatsClimateSettingSchedules { client: Client + readonly defaults: Required constructor(apiKeyOrOptions: string | SeamHttpOptions = {}) { - const clientOptions = parseOptions(apiKeyOrOptions) - this.client = createClient(clientOptions) + const options = parseOptions(apiKeyOrOptions) + this.client = 'client' in options ? options.client : createClient(options) + this.defaults = limitToSeamHttpRequestOptions(options) } static fromClient( @@ -78,6 +84,11 @@ export class SeamHttpThermostatsClimateSettingSchedules { ): Promise { warnOnInsecureuserIdentifierKey(userIdentifierKey) const clientOptions = parseOptions({ ...options, publishableKey }) + if (isSeamHttpOptionsWithClient(clientOptions)) { + throw new SeamHttpInvalidOptionsError( + 'The client option cannot be used with SeamHttp.fromPublishableKey', + ) + } const client = createClient(clientOptions) const clientSessions = SeamHttpClientSessions.fromClient(client) const { token } = await clientSessions.getOrCreate({ diff --git a/src/lib/seam/connect/routes/thermostats.ts b/src/lib/seam/connect/routes/thermostats.ts index 2eab9da0..d50abf33 100644 --- a/src/lib/seam/connect/routes/thermostats.ts +++ b/src/lib/seam/connect/routes/thermostats.ts @@ -22,18 +22,24 @@ import { type SeamHttpOptionsWithClientSessionToken, type SeamHttpOptionsWithConsoleSessionToken, type SeamHttpOptionsWithPersonalAccessToken, + type SeamHttpRequestOptions, } from 'lib/seam/connect/options.js' -import { parseOptions } from 'lib/seam/connect/parse-options.js' +import { + limitToSeamHttpRequestOptions, + parseOptions, +} from 'lib/seam/connect/parse-options.js' import { SeamHttpClientSessions } from './client-sessions.js' import { SeamHttpThermostatsClimateSettingSchedules } from './thermostats-climate-setting-schedules.js' export class SeamHttpThermostats { client: Client + readonly defaults: Required constructor(apiKeyOrOptions: string | SeamHttpOptions = {}) { - const clientOptions = parseOptions(apiKeyOrOptions) - this.client = createClient(clientOptions) + const options = parseOptions(apiKeyOrOptions) + this.client = 'client' in options ? options.client : createClient(options) + this.defaults = limitToSeamHttpRequestOptions(options) } static fromClient( @@ -79,6 +85,11 @@ export class SeamHttpThermostats { ): Promise { warnOnInsecureuserIdentifierKey(userIdentifierKey) const clientOptions = parseOptions({ ...options, publishableKey }) + if (isSeamHttpOptionsWithClient(clientOptions)) { + throw new SeamHttpInvalidOptionsError( + 'The client option cannot be used with SeamHttp.fromPublishableKey', + ) + } const client = createClient(clientOptions) const clientSessions = SeamHttpClientSessions.fromClient(client) const { token } = await clientSessions.getOrCreate({ @@ -122,7 +133,10 @@ export class SeamHttpThermostats { } get climateSettingSchedules(): SeamHttpThermostatsClimateSettingSchedules { - return SeamHttpThermostatsClimateSettingSchedules.fromClient(this.client) + return SeamHttpThermostatsClimateSettingSchedules.fromClient( + this.client, + this.defaults, + ) } async cool(body?: ThermostatsCoolBody): Promise { diff --git a/src/lib/seam/connect/routes/user-identities.ts b/src/lib/seam/connect/routes/user-identities.ts index 96e55dec..da272feb 100644 --- a/src/lib/seam/connect/routes/user-identities.ts +++ b/src/lib/seam/connect/routes/user-identities.ts @@ -22,17 +22,23 @@ import { type SeamHttpOptionsWithClientSessionToken, type SeamHttpOptionsWithConsoleSessionToken, type SeamHttpOptionsWithPersonalAccessToken, + type SeamHttpRequestOptions, } from 'lib/seam/connect/options.js' -import { parseOptions } from 'lib/seam/connect/parse-options.js' +import { + limitToSeamHttpRequestOptions, + parseOptions, +} from 'lib/seam/connect/parse-options.js' import { SeamHttpClientSessions } from './client-sessions.js' export class SeamHttpUserIdentities { client: Client + readonly defaults: Required constructor(apiKeyOrOptions: string | SeamHttpOptions = {}) { - const clientOptions = parseOptions(apiKeyOrOptions) - this.client = createClient(clientOptions) + const options = parseOptions(apiKeyOrOptions) + this.client = 'client' in options ? options.client : createClient(options) + this.defaults = limitToSeamHttpRequestOptions(options) } static fromClient( @@ -78,6 +84,11 @@ export class SeamHttpUserIdentities { ): Promise { warnOnInsecureuserIdentifierKey(userIdentifierKey) const clientOptions = parseOptions({ ...options, publishableKey }) + if (isSeamHttpOptionsWithClient(clientOptions)) { + throw new SeamHttpInvalidOptionsError( + 'The client option cannot be used with SeamHttp.fromPublishableKey', + ) + } const client = createClient(clientOptions) const clientSessions = SeamHttpClientSessions.fromClient(client) const { token } = await clientSessions.getOrCreate({ diff --git a/src/lib/seam/connect/routes/webhooks.ts b/src/lib/seam/connect/routes/webhooks.ts index 81a1304b..c7850bc3 100644 --- a/src/lib/seam/connect/routes/webhooks.ts +++ b/src/lib/seam/connect/routes/webhooks.ts @@ -26,17 +26,23 @@ import { type SeamHttpOptionsWithClientSessionToken, type SeamHttpOptionsWithConsoleSessionToken, type SeamHttpOptionsWithPersonalAccessToken, + type SeamHttpRequestOptions, } from 'lib/seam/connect/options.js' -import { parseOptions } from 'lib/seam/connect/parse-options.js' +import { + limitToSeamHttpRequestOptions, + parseOptions, +} from 'lib/seam/connect/parse-options.js' import { SeamHttpClientSessions } from './client-sessions.js' export class SeamHttpWebhooks { client: Client + readonly defaults: Required constructor(apiKeyOrOptions: string | SeamHttpOptions = {}) { - const clientOptions = parseOptions(apiKeyOrOptions) - this.client = createClient(clientOptions) + const options = parseOptions(apiKeyOrOptions) + this.client = 'client' in options ? options.client : createClient(options) + this.defaults = limitToSeamHttpRequestOptions(options) } static fromClient( @@ -82,6 +88,11 @@ export class SeamHttpWebhooks { ): Promise { warnOnInsecureuserIdentifierKey(userIdentifierKey) const clientOptions = parseOptions({ ...options, publishableKey }) + if (isSeamHttpOptionsWithClient(clientOptions)) { + throw new SeamHttpInvalidOptionsError( + 'The client option cannot be used with SeamHttp.fromPublishableKey', + ) + } const client = createClient(clientOptions) const clientSessions = SeamHttpClientSessions.fromClient(client) const { token } = await clientSessions.getOrCreate({ diff --git a/src/lib/seam/connect/routes/workspaces.ts b/src/lib/seam/connect/routes/workspaces.ts index abbf5ee8..6a48b806 100644 --- a/src/lib/seam/connect/routes/workspaces.ts +++ b/src/lib/seam/connect/routes/workspaces.ts @@ -26,17 +26,23 @@ import { type SeamHttpOptionsWithClientSessionToken, type SeamHttpOptionsWithConsoleSessionToken, type SeamHttpOptionsWithPersonalAccessToken, + type SeamHttpRequestOptions, } from 'lib/seam/connect/options.js' -import { parseOptions } from 'lib/seam/connect/parse-options.js' +import { + limitToSeamHttpRequestOptions, + parseOptions, +} from 'lib/seam/connect/parse-options.js' import { SeamHttpClientSessions } from './client-sessions.js' export class SeamHttpWorkspaces { client: Client + readonly defaults: Required constructor(apiKeyOrOptions: string | SeamHttpOptions = {}) { - const clientOptions = parseOptions(apiKeyOrOptions) - this.client = createClient(clientOptions) + const options = parseOptions(apiKeyOrOptions) + this.client = 'client' in options ? options.client : createClient(options) + this.defaults = limitToSeamHttpRequestOptions(options) } static fromClient( @@ -82,6 +88,11 @@ export class SeamHttpWorkspaces { ): Promise { warnOnInsecureuserIdentifierKey(userIdentifierKey) const clientOptions = parseOptions({ ...options, publishableKey }) + if (isSeamHttpOptionsWithClient(clientOptions)) { + throw new SeamHttpInvalidOptionsError( + 'The client option cannot be used with SeamHttp.fromPublishableKey', + ) + } const client = createClient(clientOptions) const clientSessions = SeamHttpClientSessions.fromClient(client) const { token } = await clientSessions.getOrCreate({ diff --git a/src/lib/seam/connect/seam-http-multi-workspace.ts b/src/lib/seam/connect/seam-http-multi-workspace.ts index 6384e78d..c4e65abe 100644 --- a/src/lib/seam/connect/seam-http-multi-workspace.ts +++ b/src/lib/seam/connect/seam-http-multi-workspace.ts @@ -8,16 +8,19 @@ import { type SeamHttpMultiWorkspaceOptionsWithClient, type SeamHttpMultiWorkspaceOptionsWithConsoleSessionToken, type SeamHttpMultiWorkspaceOptionsWithPersonalAccessToken, + type SeamHttpRequestOptions, } from './options.js' -import { parseOptions } from './parse-options.js' +import { limitToSeamHttpRequestOptions, parseOptions } from './parse-options.js' import { SeamHttpWorkspaces } from './routes/index.js' export class SeamHttpMultiWorkspace { client: Client + readonly defaults: Required constructor(options: SeamHttpMultiWorkspaceOptions) { - const clientOptions = parseOptions(options) - this.client = createClient(clientOptions) + const opts = parseOptions(options) + this.client = 'client' in opts ? opts.client : createClient(opts) + this.defaults = limitToSeamHttpRequestOptions(opts) } static fromClient( @@ -72,6 +75,6 @@ export class SeamHttpMultiWorkspace { } get workspaces(): SeamHttpWorkspaces { - return SeamHttpWorkspaces.fromClient(this.client) + return SeamHttpWorkspaces.fromClient(this.client, this.defaults) } } diff --git a/src/lib/seam/connect/seam-http.ts b/src/lib/seam/connect/seam-http.ts index b4a4cf88..7e0bcb37 100644 --- a/src/lib/seam/connect/seam-http.ts +++ b/src/lib/seam/connect/seam-http.ts @@ -14,8 +14,9 @@ import { type SeamHttpOptionsWithClientSessionToken, type SeamHttpOptionsWithConsoleSessionToken, type SeamHttpOptionsWithPersonalAccessToken, + type SeamHttpRequestOptions, } from './options.js' -import { parseOptions } from './parse-options.js' +import { limitToSeamHttpRequestOptions, parseOptions } from './parse-options.js' import { SeamHttpAccessCodes, SeamHttpAcs, @@ -35,10 +36,12 @@ import { export class SeamHttp { client: Client + readonly defaults: Required constructor(apiKeyOrOptions: string | SeamHttpOptions = {}) { - const clientOptions = parseOptions(apiKeyOrOptions) - this.client = createClient(clientOptions) + const options = parseOptions(apiKeyOrOptions) + this.client = 'client' in options ? options.client : createClient(options) + this.defaults = limitToSeamHttpRequestOptions(options) } static fromClient( @@ -84,6 +87,11 @@ export class SeamHttp { ): Promise { warnOnInsecureuserIdentifierKey(userIdentifierKey) const clientOptions = parseOptions({ ...options, publishableKey }) + if (isSeamHttpOptionsWithClient(clientOptions)) { + throw new SeamHttpInvalidOptionsError( + 'The client option cannot be used with SeamHttp.fromPublishableKey', + ) + } const client = createClient(clientOptions) const clientSessions = SeamHttpClientSessions.fromClient(client) const { token } = await clientSessions.getOrCreate({ @@ -127,58 +135,58 @@ export class SeamHttp { } get accessCodes(): SeamHttpAccessCodes { - return SeamHttpAccessCodes.fromClient(this.client) + return SeamHttpAccessCodes.fromClient(this.client, this.defaults) } get acs(): SeamHttpAcs { - return SeamHttpAcs.fromClient(this.client) + return SeamHttpAcs.fromClient(this.client, this.defaults) } get actionAttempts(): SeamHttpActionAttempts { - return SeamHttpActionAttempts.fromClient(this.client) + return SeamHttpActionAttempts.fromClient(this.client, this.defaults) } get clientSessions(): SeamHttpClientSessions { - return SeamHttpClientSessions.fromClient(this.client) + return SeamHttpClientSessions.fromClient(this.client, this.defaults) } get connectedAccounts(): SeamHttpConnectedAccounts { - return SeamHttpConnectedAccounts.fromClient(this.client) + return SeamHttpConnectedAccounts.fromClient(this.client, this.defaults) } get connectWebviews(): SeamHttpConnectWebviews { - return SeamHttpConnectWebviews.fromClient(this.client) + return SeamHttpConnectWebviews.fromClient(this.client, this.defaults) } get devices(): SeamHttpDevices { - return SeamHttpDevices.fromClient(this.client) + return SeamHttpDevices.fromClient(this.client, this.defaults) } get events(): SeamHttpEvents { - return SeamHttpEvents.fromClient(this.client) + return SeamHttpEvents.fromClient(this.client, this.defaults) } get locks(): SeamHttpLocks { - return SeamHttpLocks.fromClient(this.client) + return SeamHttpLocks.fromClient(this.client, this.defaults) } get noiseSensors(): SeamHttpNoiseSensors { - return SeamHttpNoiseSensors.fromClient(this.client) + return SeamHttpNoiseSensors.fromClient(this.client, this.defaults) } get thermostats(): SeamHttpThermostats { - return SeamHttpThermostats.fromClient(this.client) + return SeamHttpThermostats.fromClient(this.client, this.defaults) } get userIdentities(): SeamHttpUserIdentities { - return SeamHttpUserIdentities.fromClient(this.client) + return SeamHttpUserIdentities.fromClient(this.client, this.defaults) } get webhooks(): SeamHttpWebhooks { - return SeamHttpWebhooks.fromClient(this.client) + return SeamHttpWebhooks.fromClient(this.client, this.defaults) } get workspaces(): SeamHttpWorkspaces { - return SeamHttpWorkspaces.fromClient(this.client) + return SeamHttpWorkspaces.fromClient(this.client, this.defaults) } } diff --git a/test/seam/connect/wait-for-action-attempt.test.ts b/test/seam/connect/wait-for-action-attempt.test.ts index 4593fa97..1d60bf9b 100644 --- a/test/seam/connect/wait-for-action-attempt.test.ts +++ b/test/seam/connect/wait-for-action-attempt.test.ts @@ -211,3 +211,49 @@ test('waitForActionAttempt: waits directly on returned action attempt', async (t t.is(actionAttempt.status, 'success') }) + +test('waitForActionAttempt: does not wait by default', async (t) => { + const { seed, endpoint } = await getTestServer(t) + + const seam = SeamHttp.fromApiKey(seed.seam_apikey1_token, { + endpoint, + }) + + const actionAttempt = await seam.locks.unlockDoor({ + device_id: seed.august_device_1, + }) + + t.is(actionAttempt.status, 'pending') +}) + +test('waitForActionAttempt: can set class default', async (t) => { + const { seed, endpoint } = await getTestServer(t) + + const seam = SeamHttp.fromApiKey(seed.seam_apikey1_token, { + endpoint, + waitForActionAttempt: true, + }) + + const actionAttempt = await seam.locks.unlockDoor({ + device_id: seed.august_device_1, + }) + + t.is(actionAttempt.status, 'success') +}) + +test('waitForActionAttempt: can set class default with object', async (t) => { + const { seed, endpoint } = await getTestServer(t) + + const seam = SeamHttp.fromApiKey(seed.seam_apikey1_token, { + endpoint, + waitForActionAttempt: { + timeout: 5000, + }, + }) + + const actionAttempt = await seam.locks.unlockDoor({ + device_id: seed.august_device_1, + }) + + t.is(actionAttempt.status, 'success') +})