From 9ef5562b3f1f33981c0f505b4d6feb8ebf05f9c8 Mon Sep 17 00:00:00 2001 From: fern-api <115122769+fern-api[bot]@users.noreply.github.com> Date: Wed, 17 Sep 2025 20:42:35 +0000 Subject: [PATCH] SDK regeneration --- .npmignore | 3 +- README.md | 17 +- package.json | 2 +- reference.md | 71 +++ src/Client.ts | 10 +- src/api/resources/entities/client/Client.ts | 78 ++- src/api/resources/objects/client/Client.ts | 78 +-- .../client/requests/GetObjectRequest.ts | 10 +- .../client/requests/ListObjectsRequest.ts | 16 +- src/api/resources/tasks/client/Client.ts | 66 ++- src/core/auth/AuthProvider.ts | 5 + src/core/auth/AuthRequest.ts | 9 + src/core/auth/index.ts | 2 + src/core/fetcher/Fetcher.ts | 8 +- src/core/fetcher/index.ts | 10 +- src/core/fetcher/requestWithRetries.ts | 40 +- src/core/headers.ts | 20 +- src/version.ts | 2 +- tests/mock-server/mockEndpointBuilder.ts | 13 +- tests/mock-server/withJson.ts | 12 +- tests/unit/fetcher/Fetcher.test.ts | 2 +- tests/unit/fetcher/requestWithRetries.test.ts | 103 ++++ tests/wire/entities.test.ts | 548 +++++++++++++++++- tests/wire/objects.test.ts | 246 +++++++- tests/wire/tasks.test.ts | 389 ++++++++++++- yarn.lock | 282 +++++---- 26 files changed, 1765 insertions(+), 277 deletions(-) create mode 100644 src/core/auth/AuthProvider.ts create mode 100644 src/core/auth/AuthRequest.ts diff --git a/.npmignore b/.npmignore index 6db0876..383dd36 100644 --- a/.npmignore +++ b/.npmignore @@ -6,4 +6,5 @@ tests .fernignore .prettierrc.yml tsconfig.json -yarn.lock \ No newline at end of file +yarn.lock +pnpm-lock.yaml \ No newline at end of file diff --git a/README.md b/README.md index 4bc27ae..81798d0 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,11 @@ -# Anduril TypeScript Library +# Lattice SDK TypeScript Library ![](https://www.anduril.com/lattice-sdk/) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=https%3A%2F%2Fgithub.com%2Fanduril%2Flattice-sdk-javascript) [![npm shield](https://img.shields.io/npm/v/@anduril-industries/lattice-sdk)](https://www.npmjs.com/package/@anduril-industries/lattice-sdk) -The Lattice SDK TypeScript library provides convenient access to the Lattice API from TypeScript. +The Lattice SDK TypeScript library provides convenient access to the Lattice SDK APIs from TypeScript. ## Documentation @@ -548,6 +549,18 @@ const response = await client.entities.longPollEntityEvents(..., { }); ``` +### Additional Query String Parameters + +If you would like to send additional query string parameters as part of the request, use the `queryParams` request option. + +```typescript +const response = await client.entities.longPollEntityEvents(..., { + queryParams: { + 'customQueryParamKey': 'custom query param value' + } +}); +``` + ### Retries The SDK is instrumented with automatic retries with exponential backoff. A request will be retried as long diff --git a/package.json b/package.json index 73a2b9b..67d76ba 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@anduril-industries/lattice-sdk", - "version": "2.2.0", + "version": "2.3.0", "private": false, "repository": "github:anduril/lattice-sdk-javascript", "license": "See LICENSE", diff --git a/reference.md b/reference.md index b42843a..081aee7 100644 --- a/reference.md +++ b/reference.md @@ -801,6 +801,77 @@ while (page.hasNextPage()) { +
client.objects.getObject(objectPath, { ...params }) -> core.BinaryResponse +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Fetches an object from your environment using the objectPath path parameter. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```typescript +await client.objects.getObject("objectPath"); +``` + +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**objectPath:** `string` — The path of the object to fetch. + +
+
+ +
+
+ +**request:** `Lattice.GetObjectRequest` + +
+
+ +
+
+ +**requestOptions:** `Objects.RequestOptions` + +
+
+
+
+ +
+
+
+
client.objects.deleteObject(objectPath) -> void
diff --git a/src/Client.ts b/src/Client.ts index 8e96c0c..e86ed20 100644 --- a/src/Client.ts +++ b/src/Client.ts @@ -16,7 +16,7 @@ export declare namespace LatticeClient { baseUrl?: core.Supplier; token?: core.Supplier; /** Additional headers to include in requests. */ - headers?: Record | undefined>; + headers?: Record | null | undefined>; } export interface RequestOptions { @@ -26,8 +26,10 @@ export declare namespace LatticeClient { maxRetries?: number; /** A hook to abort the request. */ abortSignal?: AbortSignal; + /** Additional query string parameters to include in the request. */ + queryParams?: Record; /** Additional headers to include in the request. */ - headers?: Record | undefined>; + headers?: Record | null | undefined>; } } @@ -44,8 +46,8 @@ export class LatticeClient { { "X-Fern-Language": "JavaScript", "X-Fern-SDK-Name": "@anduril-industries/lattice-sdk", - "X-Fern-SDK-Version": "2.2.0", - "User-Agent": "@anduril-industries/lattice-sdk/2.2.0", + "X-Fern-SDK-Version": "2.3.0", + "User-Agent": "@anduril-industries/lattice-sdk/2.3.0", "X-Fern-Runtime": core.RUNTIME.type, "X-Fern-Runtime-Version": core.RUNTIME.version, }, diff --git a/src/api/resources/entities/client/Client.ts b/src/api/resources/entities/client/Client.ts index 060b8ac..34fcf4c 100644 --- a/src/api/resources/entities/client/Client.ts +++ b/src/api/resources/entities/client/Client.ts @@ -15,7 +15,7 @@ export declare namespace Entities { baseUrl?: core.Supplier; token?: core.Supplier; /** Additional headers to include in requests. */ - headers?: Record | undefined>; + headers?: Record | null | undefined>; } export interface RequestOptions { @@ -25,8 +25,10 @@ export declare namespace Entities { maxRetries?: number; /** A hook to abort the request. */ abortSignal?: AbortSignal; + /** Additional query string parameters to include in the request. */ + queryParams?: Record; /** Additional headers to include in the request. */ - headers?: Record | undefined>; + headers?: Record | null | undefined>; } } @@ -69,6 +71,11 @@ export class Entities { request: Lattice.Entity, requestOptions?: Entities.RequestOptions, ): Promise> { + let _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), + requestOptions?.headers, + ); const _response = await core.fetcher({ url: core.url.join( (await core.Supplier.get(this._options.baseUrl)) ?? @@ -77,12 +84,9 @@ export class Entities { "api/v1/entities", ), method: "PUT", - headers: mergeHeaders( - this._options?.headers, - mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), - requestOptions?.headers, - ), + headers: _headers, contentType: "application/json", + queryParameters: requestOptions?.queryParams, requestType: "json", body: request, timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, @@ -147,6 +151,11 @@ export class Entities { entityId: string, requestOptions?: Entities.RequestOptions, ): Promise> { + let _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), + requestOptions?.headers, + ); const _response = await core.fetcher({ url: core.url.join( (await core.Supplier.get(this._options.baseUrl)) ?? @@ -155,11 +164,8 @@ export class Entities { `api/v1/entities/${encodeURIComponent(entityId)}`, ), method: "GET", - headers: mergeHeaders( - this._options?.headers, - mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), - requestOptions?.headers, - ), + headers: _headers, + queryParameters: requestOptions?.queryParams, timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, maxRetries: requestOptions?.maxRetries, abortSignal: requestOptions?.abortSignal, @@ -240,6 +246,11 @@ export class Entities { request: Lattice.EntityOverride = {}, requestOptions?: Entities.RequestOptions, ): Promise> { + let _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), + requestOptions?.headers, + ); const _response = await core.fetcher({ url: core.url.join( (await core.Supplier.get(this._options.baseUrl)) ?? @@ -248,12 +259,9 @@ export class Entities { `api/v1/entities/${encodeURIComponent(entityId)}/override/${encodeURIComponent(fieldPath)}`, ), method: "PUT", - headers: mergeHeaders( - this._options?.headers, - mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), - requestOptions?.headers, - ), + headers: _headers, contentType: "application/json", + queryParameters: requestOptions?.queryParams, requestType: "json", body: request, timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, @@ -327,6 +335,11 @@ export class Entities { fieldPath: string, requestOptions?: Entities.RequestOptions, ): Promise> { + let _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), + requestOptions?.headers, + ); const _response = await core.fetcher({ url: core.url.join( (await core.Supplier.get(this._options.baseUrl)) ?? @@ -335,11 +348,8 @@ export class Entities { `api/v1/entities/${encodeURIComponent(entityId)}/override/${encodeURIComponent(fieldPath)}`, ), method: "DELETE", - headers: mergeHeaders( - this._options?.headers, - mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), - requestOptions?.headers, - ), + headers: _headers, + queryParameters: requestOptions?.queryParams, timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, maxRetries: requestOptions?.maxRetries, abortSignal: requestOptions?.abortSignal, @@ -420,6 +430,11 @@ export class Entities { request: Lattice.EntityEventRequest, requestOptions?: Entities.RequestOptions, ): Promise> { + let _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), + requestOptions?.headers, + ); const _response = await core.fetcher({ url: core.url.join( (await core.Supplier.get(this._options.baseUrl)) ?? @@ -428,12 +443,9 @@ export class Entities { "api/v1/entities/events", ), method: "POST", - headers: mergeHeaders( - this._options?.headers, - mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), - requestOptions?.headers, - ), + headers: _headers, contentType: "application/json", + queryParameters: requestOptions?.queryParams, requestType: "json", body: request, timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, @@ -496,6 +508,11 @@ export class Entities { request: Lattice.EntityStreamRequest = {}, requestOptions?: Entities.RequestOptions, ): Promise>> { + let _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), + requestOptions?.headers, + ); const _response = await core.fetcher({ url: core.url.join( (await core.Supplier.get(this._options.baseUrl)) ?? @@ -504,12 +521,9 @@ export class Entities { "api/v1/entities/stream", ), method: "POST", - headers: mergeHeaders( - this._options?.headers, - mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), - requestOptions?.headers, - ), + headers: _headers, contentType: "application/json", + queryParameters: requestOptions?.queryParams, requestType: "json", body: request, responseType: "sse", diff --git a/src/api/resources/objects/client/Client.ts b/src/api/resources/objects/client/Client.ts index 74b2bbe..c8d6645 100644 --- a/src/api/resources/objects/client/Client.ts +++ b/src/api/resources/objects/client/Client.ts @@ -15,7 +15,7 @@ export declare namespace Objects { baseUrl?: core.Supplier; token?: core.Supplier; /** Additional headers to include in requests. */ - headers?: Record | undefined>; + headers?: Record | null | undefined>; } export interface RequestOptions { @@ -25,8 +25,10 @@ export declare namespace Objects { maxRetries?: number; /** A hook to abort the request. */ abortSignal?: AbortSignal; + /** Additional query string parameters to include in the request. */ + queryParams?: Record; /** Additional headers to include in the request. */ - headers?: Record | undefined>; + headers?: Record | null | undefined>; } } @@ -73,6 +75,11 @@ export class Objects { if (allObjectsInMesh != null) { _queryParams["allObjectsInMesh"] = allObjectsInMesh.toString(); } + let _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), + requestOptions?.headers, + ); const _response = await core.fetcher({ url: core.url.join( (await core.Supplier.get(this._options.baseUrl)) ?? @@ -81,12 +88,8 @@ export class Objects { "api/v1/objects", ), method: "GET", - headers: mergeHeaders( - this._options?.headers, - mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), - requestOptions?.headers, - ), - queryParameters: _queryParams, + headers: _headers, + queryParameters: { ..._queryParams, ...requestOptions?.queryParams }, timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, maxRetries: requestOptions?.maxRetries, @@ -165,7 +168,16 @@ export class Objects { request: Lattice.GetObjectRequest = {}, requestOptions?: Objects.RequestOptions, ): Promise> { - const { "Accept-Encoding": acceptEncoding } = request; + const { "Accept-Encoding": acceptEncoding, Priority: priority } = request; + let _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ + Authorization: await this._getAuthorizationHeader(), + "Accept-Encoding": acceptEncoding != null ? acceptEncoding : undefined, + Priority: priority != null ? priority : undefined, + }), + requestOptions?.headers, + ); const _response = await core.fetcher({ url: core.url.join( (await core.Supplier.get(this._options.baseUrl)) ?? @@ -174,14 +186,8 @@ export class Objects { `api/v1/objects/${encodeURIComponent(objectPath)}`, ), method: "GET", - headers: mergeHeaders( - this._options?.headers, - mergeOnlyDefinedHeaders({ - Authorization: await this._getAuthorizationHeader(), - "Accept-Encoding": acceptEncoding != null ? acceptEncoding : undefined, - }), - requestOptions?.headers, - ), + headers: _headers, + queryParameters: requestOptions?.queryParams, responseType: "binary-response", timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, maxRetries: requestOptions?.maxRetries, @@ -254,6 +260,12 @@ export class Objects { requestOptions?: Objects.RequestOptions, ): Promise> { const _binaryUploadRequest = await core.file.toBinaryUploadRequest(uploadable); + let _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), + _binaryUploadRequest.headers, + requestOptions?.headers, + ); const _response = await core.fetcher({ url: core.url.join( (await core.Supplier.get(this._options.baseUrl)) ?? @@ -262,13 +274,9 @@ export class Objects { `api/v1/objects/${encodeURIComponent(objectPath)}`, ), method: "POST", - headers: mergeHeaders( - this._options?.headers, - mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), - _binaryUploadRequest.headers, - requestOptions?.headers, - ), + headers: _headers, contentType: "application/octet-stream", + queryParameters: requestOptions?.queryParams, requestType: "bytes", duplex: "half", body: _binaryUploadRequest.body, @@ -342,6 +350,11 @@ export class Objects { objectPath: string, requestOptions?: Objects.RequestOptions, ): Promise> { + let _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), + requestOptions?.headers, + ); const _response = await core.fetcher({ url: core.url.join( (await core.Supplier.get(this._options.baseUrl)) ?? @@ -350,11 +363,8 @@ export class Objects { `api/v1/objects/${encodeURIComponent(objectPath)}`, ), method: "DELETE", - headers: mergeHeaders( - this._options?.headers, - mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), - requestOptions?.headers, - ), + headers: _headers, + queryParameters: requestOptions?.queryParams, timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, maxRetries: requestOptions?.maxRetries, abortSignal: requestOptions?.abortSignal, @@ -425,6 +435,11 @@ export class Objects { objectPath: string, requestOptions?: Objects.RequestOptions, ): Promise> { + let _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), + requestOptions?.headers, + ); const _response = await core.fetcher({ url: core.url.join( (await core.Supplier.get(this._options.baseUrl)) ?? @@ -433,11 +448,8 @@ export class Objects { `api/v1/objects/${encodeURIComponent(objectPath)}`, ), method: "HEAD", - headers: mergeHeaders( - this._options?.headers, - mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), - requestOptions?.headers, - ), + headers: _headers, + queryParameters: requestOptions?.queryParams, timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, maxRetries: requestOptions?.maxRetries, abortSignal: requestOptions?.abortSignal, diff --git a/src/api/resources/objects/client/requests/GetObjectRequest.ts b/src/api/resources/objects/client/requests/GetObjectRequest.ts index 025dbd6..9bc6c24 100644 --- a/src/api/resources/objects/client/requests/GetObjectRequest.ts +++ b/src/api/resources/objects/client/requests/GetObjectRequest.ts @@ -4,9 +4,13 @@ import * as Lattice from "../../../../index.js"; +/** + * @example + * {} + */ export interface GetObjectRequest { - /** - * If set, Lattice will compress the response using the specified compression method. If the header is not defined, or the compression method is set to `identity`, no compression will be applied to the response. - */ + /** If set, Lattice will compress the response using the specified compression method. If the header is not defined, or the compression method is set to `identity`, no compression will be applied to the response. */ "Accept-Encoding"?: Lattice.GetObjectRequestAcceptEncoding; + /** Indicates a client's preference for the priority of the response. The value is a structured header as defined in RFC 9218. If you do not set the header, Lattice uses the default priority set for the environment. Incremental delivery directives are not supported and will be ignored. */ + Priority?: string; } diff --git a/src/api/resources/objects/client/requests/ListObjectsRequest.ts b/src/api/resources/objects/client/requests/ListObjectsRequest.ts index 73d77b9..f6b970f 100644 --- a/src/api/resources/objects/client/requests/ListObjectsRequest.ts +++ b/src/api/resources/objects/client/requests/ListObjectsRequest.ts @@ -7,20 +7,12 @@ * {} */ export interface ListObjectsRequest { - /** - * Filters the objects based on the specified prefix path. If no path is specified, all objects are returned. - */ + /** Filters the objects based on the specified prefix path. If no path is specified, all objects are returned. */ prefix?: string; - /** - * Sets the age for the oldest objects to query across the environment. - */ + /** Sets the age for the oldest objects to query across the environment. */ sinceTimestamp?: string; - /** - * Base64 and URL-encoded cursor returned by the service to continue paging. - */ + /** Base64 and URL-encoded cursor returned by the service to continue paging. */ pageToken?: string; - /** - * Lists objects across all environment nodes in a Lattice Mesh. - */ + /** Lists objects across all environment nodes in a Lattice Mesh. */ allObjectsInMesh?: boolean; } diff --git a/src/api/resources/tasks/client/Client.ts b/src/api/resources/tasks/client/Client.ts index c6080cc..570f137 100644 --- a/src/api/resources/tasks/client/Client.ts +++ b/src/api/resources/tasks/client/Client.ts @@ -15,7 +15,7 @@ export declare namespace Tasks { baseUrl?: core.Supplier; token?: core.Supplier; /** Additional headers to include in requests. */ - headers?: Record | undefined>; + headers?: Record | null | undefined>; } export interface RequestOptions { @@ -25,8 +25,10 @@ export declare namespace Tasks { maxRetries?: number; /** A hook to abort the request. */ abortSignal?: AbortSignal; + /** Additional query string parameters to include in the request. */ + queryParams?: Record; /** Additional headers to include in the request. */ - headers?: Record | undefined>; + headers?: Record | null | undefined>; } } @@ -64,6 +66,11 @@ export class Tasks { request: Lattice.TaskCreation = {}, requestOptions?: Tasks.RequestOptions, ): Promise> { + let _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), + requestOptions?.headers, + ); const _response = await core.fetcher({ url: core.url.join( (await core.Supplier.get(this._options.baseUrl)) ?? @@ -72,12 +79,9 @@ export class Tasks { "api/v1/tasks", ), method: "POST", - headers: mergeHeaders( - this._options?.headers, - mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), - requestOptions?.headers, - ), + headers: _headers, contentType: "application/json", + queryParameters: requestOptions?.queryParams, requestType: "json", body: request, timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, @@ -139,6 +143,11 @@ export class Tasks { taskId: string, requestOptions?: Tasks.RequestOptions, ): Promise> { + let _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), + requestOptions?.headers, + ); const _response = await core.fetcher({ url: core.url.join( (await core.Supplier.get(this._options.baseUrl)) ?? @@ -147,11 +156,8 @@ export class Tasks { `api/v1/tasks/${encodeURIComponent(taskId)}`, ), method: "GET", - headers: mergeHeaders( - this._options?.headers, - mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), - requestOptions?.headers, - ), + headers: _headers, + queryParameters: requestOptions?.queryParams, timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, maxRetries: requestOptions?.maxRetries, abortSignal: requestOptions?.abortSignal, @@ -221,6 +227,11 @@ export class Tasks { request: Lattice.TaskStatusUpdate = {}, requestOptions?: Tasks.RequestOptions, ): Promise> { + let _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), + requestOptions?.headers, + ); const _response = await core.fetcher({ url: core.url.join( (await core.Supplier.get(this._options.baseUrl)) ?? @@ -229,12 +240,9 @@ export class Tasks { `api/v1/tasks/${encodeURIComponent(taskId)}/status`, ), method: "PUT", - headers: mergeHeaders( - this._options?.headers, - mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), - requestOptions?.headers, - ), + headers: _headers, contentType: "application/json", + queryParameters: requestOptions?.queryParams, requestType: "json", body: request, timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, @@ -305,6 +313,11 @@ export class Tasks { request: Lattice.TaskQuery = {}, requestOptions?: Tasks.RequestOptions, ): Promise> { + let _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), + requestOptions?.headers, + ); const _response = await core.fetcher({ url: core.url.join( (await core.Supplier.get(this._options.baseUrl)) ?? @@ -313,12 +326,9 @@ export class Tasks { "api/v1/tasks/query", ), method: "POST", - headers: mergeHeaders( - this._options?.headers, - mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), - requestOptions?.headers, - ), + headers: _headers, contentType: "application/json", + queryParameters: requestOptions?.queryParams, requestType: "json", body: request, timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, @@ -388,6 +398,11 @@ export class Tasks { request: Lattice.AgentListener = {}, requestOptions?: Tasks.RequestOptions, ): Promise> { + let _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), + requestOptions?.headers, + ); const _response = await core.fetcher({ url: core.url.join( (await core.Supplier.get(this._options.baseUrl)) ?? @@ -396,12 +411,9 @@ export class Tasks { "api/v1/agent/listen", ), method: "POST", - headers: mergeHeaders( - this._options?.headers, - mergeOnlyDefinedHeaders({ Authorization: await this._getAuthorizationHeader() }), - requestOptions?.headers, - ), + headers: _headers, contentType: "application/json", + queryParameters: requestOptions?.queryParams, requestType: "json", body: request, timeoutMs: requestOptions?.timeoutInSeconds != null ? requestOptions.timeoutInSeconds * 1000 : 60000, diff --git a/src/core/auth/AuthProvider.ts b/src/core/auth/AuthProvider.ts new file mode 100644 index 0000000..07e0d6a --- /dev/null +++ b/src/core/auth/AuthProvider.ts @@ -0,0 +1,5 @@ +import { AuthRequest } from "./AuthRequest.js"; + +export interface AuthProvider { + getAuthRequest(): Promise; +} diff --git a/src/core/auth/AuthRequest.ts b/src/core/auth/AuthRequest.ts new file mode 100644 index 0000000..f6218b4 --- /dev/null +++ b/src/core/auth/AuthRequest.ts @@ -0,0 +1,9 @@ +/** + * Request parameters for authentication requests. + */ +export interface AuthRequest { + /** + * The headers to be included in the request. + */ + headers: Record; +} diff --git a/src/core/auth/index.ts b/src/core/auth/index.ts index 59c0fe7..7faab9d 100644 --- a/src/core/auth/index.ts +++ b/src/core/auth/index.ts @@ -1,2 +1,4 @@ +export { AuthProvider } from "./AuthProvider.js"; +export { type AuthRequest } from "./AuthRequest.js"; export { BasicAuth } from "./BasicAuth.js"; export { BearerToken } from "./BearerToken.js"; diff --git a/src/core/fetcher/Fetcher.ts b/src/core/fetcher/Fetcher.ts index dd9a40f..39e6914 100644 --- a/src/core/fetcher/Fetcher.ts +++ b/src/core/fetcher/Fetcher.ts @@ -1,14 +1,14 @@ import { toJson } from "../json.js"; import { APIResponse } from "./APIResponse.js"; -import { abortRawResponse, toRawResponse, unknownRawResponse } from "./RawResponse.js"; -import { Supplier } from "./Supplier.js"; import { createRequestUrl } from "./createRequestUrl.js"; import { getErrorResponseBody } from "./getErrorResponseBody.js"; import { getFetchFn } from "./getFetchFn.js"; import { getRequestBody } from "./getRequestBody.js"; import { getResponseBody } from "./getResponseBody.js"; import { makeRequest } from "./makeRequest.js"; +import { abortRawResponse, toRawResponse, unknownRawResponse } from "./RawResponse.js"; import { requestWithRetries } from "./requestWithRetries.js"; +import { Supplier } from "./Supplier.js"; export type FetchFunction = (args: Fetcher.Args) => Promise>; @@ -17,8 +17,8 @@ export declare namespace Fetcher { url: string; method: string; contentType?: string; - headers?: Record | undefined>; - queryParameters?: Record; + headers?: Record | null | undefined>; + queryParameters?: Record; body?: unknown; timeoutMs?: number; maxRetries?: number; diff --git a/src/core/fetcher/index.ts b/src/core/fetcher/index.ts index 49e1393..a131e34 100644 --- a/src/core/fetcher/index.ts +++ b/src/core/fetcher/index.ts @@ -1,9 +1,9 @@ export type { APIResponse } from "./APIResponse.js"; -export { fetcher } from "./Fetcher.js"; +export type { BinaryResponse } from "./BinaryResponse.js"; export type { Fetcher, FetchFunction } from "./Fetcher.js"; +export { fetcher } from "./Fetcher.js"; export { getHeader } from "./getHeader.js"; -export { Supplier } from "./Supplier.js"; -export { abortRawResponse, toRawResponse, unknownRawResponse } from "./RawResponse.js"; -export type { RawResponse, WithRawResponse } from "./RawResponse.js"; export { HttpResponsePromise } from "./HttpResponsePromise.js"; -export { BinaryResponse } from "./BinaryResponse.js"; +export type { RawResponse, WithRawResponse } from "./RawResponse.js"; +export { abortRawResponse, toRawResponse, unknownRawResponse } from "./RawResponse.js"; +export { Supplier } from "./Supplier.js"; diff --git a/src/core/fetcher/requestWithRetries.ts b/src/core/fetcher/requestWithRetries.ts index add3cce..6728d33 100644 --- a/src/core/fetcher/requestWithRetries.ts +++ b/src/core/fetcher/requestWithRetries.ts @@ -9,6 +9,42 @@ function addJitter(delay: number): number { return delay * jitterMultiplier; } +function getRetryDelayFromHeaders(response: Response, retryAttempt: number): number { + // Check for Retry-After header first (RFC 7231) + const retryAfter = response.headers.get("Retry-After"); + if (retryAfter) { + // Parse as number of seconds... + const retryAfterSeconds = parseInt(retryAfter, 10); + if (!isNaN(retryAfterSeconds)) { + // Convert seconds to milliseconds and cap at MAX_RETRY_DELAY + return Math.min(retryAfterSeconds * 1000, MAX_RETRY_DELAY); + } + + // ...or as an HTTP date; both are valid + const retryAfterDate = new Date(retryAfter); + if (!isNaN(retryAfterDate.getTime())) { + const delay = retryAfterDate.getTime() - Date.now(); + return Math.min(Math.max(delay, 0), MAX_RETRY_DELAY); + } + } + + // Then check for industry-standard X-RateLimit-Reset header + const rateLimitReset = response.headers.get("X-RateLimit-Reset"); + if (rateLimitReset) { + const resetTime = parseInt(rateLimitReset, 10); + if (!isNaN(resetTime)) { + // Assume Unix timestamp in epoch seconds + const delay = resetTime * 1000 - Date.now(); + if (delay > 0) { + return Math.min(delay, MAX_RETRY_DELAY); + } + } + } + + // Fall back to exponential backoff + return Math.min(INITIAL_RETRY_DELAY * Math.pow(2, retryAttempt), MAX_RETRY_DELAY); +} + export async function requestWithRetries( requestFn: () => Promise, maxRetries: number = DEFAULT_MAX_RETRIES, @@ -17,8 +53,8 @@ export async function requestWithRetries( for (let i = 0; i < maxRetries; ++i) { if ([408, 429].includes(response.status) || response.status >= 500) { - // Calculate base delay using exponential backoff (in milliseconds) - const baseDelay = Math.min(INITIAL_RETRY_DELAY * Math.pow(2, i), MAX_RETRY_DELAY); + // Get delay from headers or fall back to exponential backoff + const baseDelay = getRetryDelayFromHeaders(response, i); // Add jitter to the delay const delayWithJitter = addJitter(baseDelay); diff --git a/src/core/headers.ts b/src/core/headers.ts index 561314d..8583614 100644 --- a/src/core/headers.ts +++ b/src/core/headers.ts @@ -1,9 +1,13 @@ import * as core from "./index.js"; export function mergeHeaders( - ...headersArray: (Record | undefined> | undefined)[] -): Record> { - const result: Record> = {}; + ...headersArray: ( + | Record | null | undefined> + | null + | undefined + )[] +): Record> { + const result: Record> = {}; for (const [key, value] of headersArray .filter((headers) => headers != null) @@ -19,9 +23,13 @@ export function mergeHeaders( } export function mergeOnlyDefinedHeaders( - ...headersArray: (Record | undefined> | undefined)[] -): Record> { - const result: Record> = {}; + ...headersArray: ( + | Record | null | undefined> + | null + | undefined + )[] +): Record> { + const result: Record> = {}; for (const [key, value] of headersArray .filter((headers) => headers != null) diff --git a/src/version.ts b/src/version.ts index 13d9985..af160ad 100644 --- a/src/version.ts +++ b/src/version.ts @@ -1 +1 @@ -export const SDK_VERSION = "2.2.0"; +export const SDK_VERSION = "2.3.0"; diff --git a/tests/mock-server/mockEndpointBuilder.ts b/tests/mock-server/mockEndpointBuilder.ts index 76ed83c..88368d4 100644 --- a/tests/mock-server/mockEndpointBuilder.ts +++ b/tests/mock-server/mockEndpointBuilder.ts @@ -128,6 +128,9 @@ class RequestBuilder implements MethodStage, RequestHeadersStage, RequestBodySta } jsonBody(body: unknown): ResponseStage { + if (body === undefined) { + throw new Error("Undefined is not valid JSON. Do not call jsonBody if you want an empty body."); + } this.predicates.push((resolver) => withJson(body, resolver)); return this; } @@ -179,16 +182,24 @@ class ResponseBuilder implements ResponseStatusStage, ResponseHeaderStage, Respo } public jsonBody(body: unknown): BuildStage { + if (body === undefined) { + throw new Error("Undefined is not valid JSON. Do not call jsonBody if you expect an empty body."); + } this.responseBody = toJson(body); return this; } public build(): HttpHandler { const responseResolver: HttpResponseResolver = () => { - return new HttpResponse(this.responseBody, { + const response = new HttpResponse(this.responseBody, { status: this.responseStatusCode, headers: this.responseHeaders, }); + // if no Content-Type header is set, delete the default text content type that is set + if (Object.keys(this.responseHeaders).some((key) => key.toLowerCase() === "content-type") === false) { + response.headers.delete("Content-Type"); + } + return response; }; const finalResolver = this.requestPredicates.reduceRight((acc, predicate) => predicate(acc), responseResolver); diff --git a/tests/mock-server/withJson.ts b/tests/mock-server/withJson.ts index 44e3eb8..03f585d 100644 --- a/tests/mock-server/withJson.ts +++ b/tests/mock-server/withJson.ts @@ -12,17 +12,23 @@ export function withJson(expectedBody: unknown, resolver: HttpResponseResolver): const { request } = args; let clonedRequest: Request; + let bodyText: string | undefined; let actualBody: unknown; try { clonedRequest = request.clone(); - actualBody = fromJson(await clonedRequest.text()); + bodyText = await clonedRequest.text(); + if (bodyText === "") { + console.error("Request body is empty, expected a JSON object."); + return passthrough(); + } + actualBody = fromJson(bodyText); } catch (error) { - console.error("Error processing request body:", error); + console.error(`Error processing request body:\n\tError: ${error}\n\tBody: ${bodyText}`); return passthrough(); } const mismatches = findMismatches(actualBody, expectedBody); - if (Object.keys(mismatches).length > 0) { + if (Object.keys(mismatches).filter((key) => !key.startsWith("pagination.")).length > 0) { console.error("JSON body mismatch:", toJson(mismatches, undefined, 2)); return passthrough(); } diff --git a/tests/unit/fetcher/Fetcher.test.ts b/tests/unit/fetcher/Fetcher.test.ts index a9bd945..f983f08 100644 --- a/tests/unit/fetcher/Fetcher.test.ts +++ b/tests/unit/fetcher/Fetcher.test.ts @@ -3,7 +3,7 @@ import stream from "stream"; import { join } from "path"; import { Fetcher, fetcherImpl } from "../../../src/core/fetcher/Fetcher"; -import { BinaryResponse } from "../../../src/core"; +import type { BinaryResponse } from "../../../src/core"; describe("Test fetcherImpl", () => { it("should handle successful request", async () => { diff --git a/tests/unit/fetcher/requestWithRetries.test.ts b/tests/unit/fetcher/requestWithRetries.test.ts index 3cdaa40..779f4fb 100644 --- a/tests/unit/fetcher/requestWithRetries.test.ts +++ b/tests/unit/fetcher/requestWithRetries.test.ts @@ -129,4 +129,107 @@ describe("requestWithRetries", () => { expect(response1.status).toBe(200); expect(response2.status).toBe(200); }); + + it("should respect retry-after header with seconds value", async () => { + setTimeoutSpy = jest.spyOn(global, "setTimeout").mockImplementation((callback: (args: void) => void) => { + process.nextTick(callback); + return null as any; + }); + + mockFetch + .mockResolvedValueOnce( + new Response("", { + status: 429, + headers: new Headers({ "retry-after": "5" }), + }), + ) + .mockResolvedValueOnce(new Response("", { status: 200 })); + + const responsePromise = requestWithRetries(() => mockFetch(), 1); + await jest.runAllTimersAsync(); + const response = await responsePromise; + + expect(setTimeoutSpy).toHaveBeenCalledWith(expect.any(Function), 5000); // 5 seconds = 5000ms + expect(response.status).toBe(200); + }); + + it("should respect retry-after header with HTTP date value", async () => { + setTimeoutSpy = jest.spyOn(global, "setTimeout").mockImplementation((callback: (args: void) => void) => { + process.nextTick(callback); + return null as any; + }); + + const futureDate = new Date(Date.now() + 3000); // 3 seconds from now + mockFetch + .mockResolvedValueOnce( + new Response("", { + status: 429, + headers: new Headers({ "retry-after": futureDate.toUTCString() }), + }), + ) + .mockResolvedValueOnce(new Response("", { status: 200 })); + + const responsePromise = requestWithRetries(() => mockFetch(), 1); + await jest.runAllTimersAsync(); + const response = await responsePromise; + + // Should use the date-based delay (approximately 3000ms, but with jitter) + expect(setTimeoutSpy).toHaveBeenCalledWith(expect.any(Function), expect.any(Number)); + const actualDelay = setTimeoutSpy.mock.calls[0][1]; + expect(actualDelay).toBeGreaterThan(2000); + expect(actualDelay).toBeLessThan(4000); + expect(response.status).toBe(200); + }); + + it("should respect x-ratelimit-reset header", async () => { + setTimeoutSpy = jest.spyOn(global, "setTimeout").mockImplementation((callback: (args: void) => void) => { + process.nextTick(callback); + return null as any; + }); + + const resetTime = Math.floor((Date.now() + 4000) / 1000); // 4 seconds from now in Unix timestamp + mockFetch + .mockResolvedValueOnce( + new Response("", { + status: 429, + headers: new Headers({ "x-ratelimit-reset": resetTime.toString() }), + }), + ) + .mockResolvedValueOnce(new Response("", { status: 200 })); + + const responsePromise = requestWithRetries(() => mockFetch(), 1); + await jest.runAllTimersAsync(); + const response = await responsePromise; + + // Should use the x-ratelimit-reset delay (approximately 4000ms, but with jitter) + expect(setTimeoutSpy).toHaveBeenCalledWith(expect.any(Function), expect.any(Number)); + const actualDelay = setTimeoutSpy.mock.calls[0][1]; + expect(actualDelay).toBeGreaterThan(3000); + expect(actualDelay).toBeLessThan(5000); + expect(response.status).toBe(200); + }); + + it("should cap delay at MAX_RETRY_DELAY for large header values", async () => { + setTimeoutSpy = jest.spyOn(global, "setTimeout").mockImplementation((callback: (args: void) => void) => { + process.nextTick(callback); + return null as any; + }); + + mockFetch + .mockResolvedValueOnce( + new Response("", { + status: 429, + headers: new Headers({ "retry-after": "120" }), // 120 seconds = 120000ms > MAX_RETRY_DELAY (60000ms) + }), + ) + .mockResolvedValueOnce(new Response("", { status: 200 })); + + const responsePromise = requestWithRetries(() => mockFetch(), 1); + await jest.runAllTimersAsync(); + const response = await responsePromise; + + // Should be capped at MAX_RETRY_DELAY (60000ms) with jitter applied + expect(setTimeoutSpy).toHaveBeenCalledWith(expect.any(Function), 60000); // Exactly MAX_RETRY_DELAY since jitter with 0.5 random keeps it at 60000 + expect(response.status).toBe(200); + }); }); diff --git a/tests/wire/entities.test.ts b/tests/wire/entities.test.ts index 0fda8b3..1241687 100644 --- a/tests/wire/entities.test.ts +++ b/tests/wire/entities.test.ts @@ -7,7 +7,7 @@ import * as Lattice from "../../src/api/index"; import { LatticeClient } from "../../src/Client"; describe("Entities", () => { - test("publishEntity", async () => { + test("publishEntity (1)", async () => { const server = mockServerPool.createServer(); const client = new LatticeClient({ token: "test", environment: server.baseUrl }); const rawRequestBody = {}; @@ -383,7 +383,205 @@ describe("Entities", () => { }); }); - test("getEntity", async () => { + test("publishEntity (2)", async () => { + const server = mockServerPool.createServer(); + const client = new LatticeClient({ token: "test", environment: server.baseUrl }); + const rawRequestBody = { + entityId: undefined, + description: undefined, + isLive: undefined, + createdTime: undefined, + expiryTime: undefined, + noExpiry: undefined, + status: undefined, + location: undefined, + locationUncertainty: undefined, + geoShape: undefined, + geoDetails: undefined, + aliases: undefined, + tracked: undefined, + correlation: undefined, + milView: undefined, + ontology: undefined, + sensors: undefined, + payloads: undefined, + powerState: undefined, + provenance: undefined, + overrides: undefined, + indicators: undefined, + targetPriority: undefined, + signal: undefined, + transponderCodes: undefined, + dataClassification: undefined, + taskCatalog: undefined, + media: undefined, + relationships: undefined, + visualDetails: undefined, + dimensions: undefined, + routeDetails: undefined, + schedules: undefined, + health: undefined, + groupDetails: undefined, + supplies: undefined, + orbit: undefined, + }; + const rawResponseBody = { key: "value" }; + server + .mockEndpoint() + .put("/api/v1/entities") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(400) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.entities.publishEntity({ + entityId: undefined, + description: undefined, + isLive: undefined, + createdTime: undefined, + expiryTime: undefined, + noExpiry: undefined, + status: undefined, + location: undefined, + locationUncertainty: undefined, + geoShape: undefined, + geoDetails: undefined, + aliases: undefined, + tracked: undefined, + correlation: undefined, + milView: undefined, + ontology: undefined, + sensors: undefined, + payloads: undefined, + powerState: undefined, + provenance: undefined, + overrides: undefined, + indicators: undefined, + targetPriority: undefined, + signal: undefined, + transponderCodes: undefined, + dataClassification: undefined, + taskCatalog: undefined, + media: undefined, + relationships: undefined, + visualDetails: undefined, + dimensions: undefined, + routeDetails: undefined, + schedules: undefined, + health: undefined, + groupDetails: undefined, + supplies: undefined, + orbit: undefined, + }); + }).rejects.toThrow( + new Lattice.BadRequestError({ + key: "value", + }), + ); + }); + + test("publishEntity (3)", async () => { + const server = mockServerPool.createServer(); + const client = new LatticeClient({ token: "test", environment: server.baseUrl }); + const rawRequestBody = { + entityId: undefined, + description: undefined, + isLive: undefined, + createdTime: undefined, + expiryTime: undefined, + noExpiry: undefined, + status: undefined, + location: undefined, + locationUncertainty: undefined, + geoShape: undefined, + geoDetails: undefined, + aliases: undefined, + tracked: undefined, + correlation: undefined, + milView: undefined, + ontology: undefined, + sensors: undefined, + payloads: undefined, + powerState: undefined, + provenance: undefined, + overrides: undefined, + indicators: undefined, + targetPriority: undefined, + signal: undefined, + transponderCodes: undefined, + dataClassification: undefined, + taskCatalog: undefined, + media: undefined, + relationships: undefined, + visualDetails: undefined, + dimensions: undefined, + routeDetails: undefined, + schedules: undefined, + health: undefined, + groupDetails: undefined, + supplies: undefined, + orbit: undefined, + }; + const rawResponseBody = { key: "value" }; + server + .mockEndpoint() + .put("/api/v1/entities") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(401) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.entities.publishEntity({ + entityId: undefined, + description: undefined, + isLive: undefined, + createdTime: undefined, + expiryTime: undefined, + noExpiry: undefined, + status: undefined, + location: undefined, + locationUncertainty: undefined, + geoShape: undefined, + geoDetails: undefined, + aliases: undefined, + tracked: undefined, + correlation: undefined, + milView: undefined, + ontology: undefined, + sensors: undefined, + payloads: undefined, + powerState: undefined, + provenance: undefined, + overrides: undefined, + indicators: undefined, + targetPriority: undefined, + signal: undefined, + transponderCodes: undefined, + dataClassification: undefined, + taskCatalog: undefined, + media: undefined, + relationships: undefined, + visualDetails: undefined, + dimensions: undefined, + routeDetails: undefined, + schedules: undefined, + health: undefined, + groupDetails: undefined, + supplies: undefined, + orbit: undefined, + }); + }).rejects.toThrow( + new Lattice.UnauthorizedError({ + key: "value", + }), + ); + }); + + test("getEntity (1)", async () => { const server = mockServerPool.createServer(); const client = new LatticeClient({ token: "test", environment: server.baseUrl }); @@ -758,7 +956,73 @@ describe("Entities", () => { }); }); - test("overrideEntity", async () => { + test("getEntity (2)", async () => { + const server = mockServerPool.createServer(); + const client = new LatticeClient({ token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + server + .mockEndpoint() + .get("/api/v1/entities/entityId") + .respondWith() + .statusCode(400) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.entities.getEntity("entityId"); + }).rejects.toThrow( + new Lattice.BadRequestError({ + key: "value", + }), + ); + }); + + test("getEntity (3)", async () => { + const server = mockServerPool.createServer(); + const client = new LatticeClient({ token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + server + .mockEndpoint() + .get("/api/v1/entities/entityId") + .respondWith() + .statusCode(401) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.entities.getEntity("entityId"); + }).rejects.toThrow( + new Lattice.UnauthorizedError({ + key: "value", + }), + ); + }); + + test("getEntity (4)", async () => { + const server = mockServerPool.createServer(); + const client = new LatticeClient({ token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + server + .mockEndpoint() + .get("/api/v1/entities/entityId") + .respondWith() + .statusCode(404) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.entities.getEntity("entityId"); + }).rejects.toThrow( + new Lattice.NotFoundError({ + key: "value", + }), + ); + }); + + test("overrideEntity (1)", async () => { const server = mockServerPool.createServer(); const client = new LatticeClient({ token: "test", environment: server.baseUrl }); const rawRequestBody = {}; @@ -1134,7 +1398,85 @@ describe("Entities", () => { }); }); - test("removeEntityOverride", async () => { + test("overrideEntity (2)", async () => { + const server = mockServerPool.createServer(); + const client = new LatticeClient({ token: "test", environment: server.baseUrl }); + const rawRequestBody = { entity: undefined, provenance: undefined }; + const rawResponseBody = { key: "value" }; + server + .mockEndpoint() + .put("/api/v1/entities/entityId/override/fieldPath") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(400) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.entities.overrideEntity("entityId", "fieldPath", { + entity: undefined, + provenance: undefined, + }); + }).rejects.toThrow( + new Lattice.BadRequestError({ + key: "value", + }), + ); + }); + + test("overrideEntity (3)", async () => { + const server = mockServerPool.createServer(); + const client = new LatticeClient({ token: "test", environment: server.baseUrl }); + const rawRequestBody = { entity: undefined, provenance: undefined }; + const rawResponseBody = { key: "value" }; + server + .mockEndpoint() + .put("/api/v1/entities/entityId/override/fieldPath") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(401) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.entities.overrideEntity("entityId", "fieldPath", { + entity: undefined, + provenance: undefined, + }); + }).rejects.toThrow( + new Lattice.UnauthorizedError({ + key: "value", + }), + ); + }); + + test("overrideEntity (4)", async () => { + const server = mockServerPool.createServer(); + const client = new LatticeClient({ token: "test", environment: server.baseUrl }); + const rawRequestBody = { entity: undefined, provenance: undefined }; + const rawResponseBody = { key: "value" }; + server + .mockEndpoint() + .put("/api/v1/entities/entityId/override/fieldPath") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(404) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.entities.overrideEntity("entityId", "fieldPath", { + entity: undefined, + provenance: undefined, + }); + }).rejects.toThrow( + new Lattice.NotFoundError({ + key: "value", + }), + ); + }); + + test("removeEntityOverride (1)", async () => { const server = mockServerPool.createServer(); const client = new LatticeClient({ token: "test", environment: server.baseUrl }); @@ -1509,7 +1851,73 @@ describe("Entities", () => { }); }); - test("longPollEntityEvents", async () => { + test("removeEntityOverride (2)", async () => { + const server = mockServerPool.createServer(); + const client = new LatticeClient({ token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + server + .mockEndpoint() + .delete("/api/v1/entities/entityId/override/fieldPath") + .respondWith() + .statusCode(400) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.entities.removeEntityOverride("entityId", "fieldPath"); + }).rejects.toThrow( + new Lattice.BadRequestError({ + key: "value", + }), + ); + }); + + test("removeEntityOverride (3)", async () => { + const server = mockServerPool.createServer(); + const client = new LatticeClient({ token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + server + .mockEndpoint() + .delete("/api/v1/entities/entityId/override/fieldPath") + .respondWith() + .statusCode(401) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.entities.removeEntityOverride("entityId", "fieldPath"); + }).rejects.toThrow( + new Lattice.UnauthorizedError({ + key: "value", + }), + ); + }); + + test("removeEntityOverride (4)", async () => { + const server = mockServerPool.createServer(); + const client = new LatticeClient({ token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + server + .mockEndpoint() + .delete("/api/v1/entities/entityId/override/fieldPath") + .respondWith() + .statusCode(404) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.entities.removeEntityOverride("entityId", "fieldPath"); + }).rejects.toThrow( + new Lattice.NotFoundError({ + key: "value", + }), + ); + }); + + test("longPollEntityEvents (1)", async () => { const server = mockServerPool.createServer(); const client = new LatticeClient({ token: "test", environment: server.baseUrl }); const rawRequestBody = { sessionToken: "sessionToken" }; @@ -1539,4 +1947,134 @@ describe("Entities", () => { ], }); }); + + test("longPollEntityEvents (2)", async () => { + const server = mockServerPool.createServer(); + const client = new LatticeClient({ token: "test", environment: server.baseUrl }); + const rawRequestBody = { sessionToken: "sessionToken", batchSize: undefined }; + const rawResponseBody = { key: "value" }; + server + .mockEndpoint() + .post("/api/v1/entities/events") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(400) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.entities.longPollEntityEvents({ + sessionToken: "sessionToken", + batchSize: undefined, + }); + }).rejects.toThrow( + new Lattice.BadRequestError({ + key: "value", + }), + ); + }); + + test("longPollEntityEvents (3)", async () => { + const server = mockServerPool.createServer(); + const client = new LatticeClient({ token: "test", environment: server.baseUrl }); + const rawRequestBody = { sessionToken: "sessionToken", batchSize: undefined }; + const rawResponseBody = { key: "value" }; + server + .mockEndpoint() + .post("/api/v1/entities/events") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(401) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.entities.longPollEntityEvents({ + sessionToken: "sessionToken", + batchSize: undefined, + }); + }).rejects.toThrow( + new Lattice.UnauthorizedError({ + key: "value", + }), + ); + }); + + test("longPollEntityEvents (4)", async () => { + const server = mockServerPool.createServer(); + const client = new LatticeClient({ token: "test", environment: server.baseUrl }); + const rawRequestBody = { sessionToken: "sessionToken", batchSize: undefined }; + const rawResponseBody = { key: "value" }; + server + .mockEndpoint() + .post("/api/v1/entities/events") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(404) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.entities.longPollEntityEvents({ + sessionToken: "sessionToken", + batchSize: undefined, + }); + }).rejects.toThrow( + new Lattice.NotFoundError({ + key: "value", + }), + ); + }); + + test("longPollEntityEvents (5)", async () => { + const server = mockServerPool.createServer(); + const client = new LatticeClient({ token: "test", environment: server.baseUrl }); + const rawRequestBody = { sessionToken: "sessionToken", batchSize: undefined }; + const rawResponseBody = { key: "value" }; + server + .mockEndpoint() + .post("/api/v1/entities/events") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(408) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.entities.longPollEntityEvents({ + sessionToken: "sessionToken", + batchSize: undefined, + }); + }).rejects.toThrow( + new Lattice.RequestTimeoutError({ + key: "value", + }), + ); + }); + + test("longPollEntityEvents (6)", async () => { + const server = mockServerPool.createServer(); + const client = new LatticeClient({ token: "test", environment: server.baseUrl }); + const rawRequestBody = { sessionToken: "sessionToken", batchSize: undefined }; + const rawResponseBody = { key: "value" }; + server + .mockEndpoint() + .post("/api/v1/entities/events") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(429) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.entities.longPollEntityEvents({ + sessionToken: "sessionToken", + batchSize: undefined, + }); + }).rejects.toThrow( + new Lattice.TooManyRequestsError({ + key: "value", + }), + ); + }); }); diff --git a/tests/wire/objects.test.ts b/tests/wire/objects.test.ts index bd24205..c86f05b 100644 --- a/tests/wire/objects.test.ts +++ b/tests/wire/objects.test.ts @@ -4,9 +4,97 @@ import { mockServerPool } from "../mock-server/MockServerPool"; import { LatticeClient } from "../../src/Client"; +import * as Lattice from "../../src/api/index"; describe("Objects", () => { - test("deleteObject", async () => { + test("listObjects (1)", async () => { + const server = mockServerPool.createServer(); + const client = new LatticeClient({ token: "test", environment: server.baseUrl }); + + const rawResponseBody = { + path_metadatas: [ + { + content_identifier: { path: "path", checksum: "checksum" }, + size_bytes: 1000000, + last_updated_at: "2024-01-15T09:30:00Z", + expiry_time: "2024-01-15T09:30:00Z", + }, + ], + next_page_token: "next_page_token", + }; + server.mockEndpoint().get("/api/v1/objects").respondWith().statusCode(200).jsonBody(rawResponseBody).build(); + + const expected = { + path_metadatas: [ + { + content_identifier: { + path: "path", + checksum: "checksum", + }, + size_bytes: 1000000, + last_updated_at: "2024-01-15T09:30:00Z", + expiry_time: "2024-01-15T09:30:00Z", + }, + ], + next_page_token: "next_page_token", + }; + const page = await client.objects.listObjects(); + expect(expected.path_metadatas).toEqual(page.data); + + expect(page.hasNextPage()).toBe(true); + const nextPage = await page.getNextPage(); + expect(expected.path_metadatas).toEqual(nextPage.data); + }); + + test("listObjects (2)", async () => { + const server = mockServerPool.createServer(); + const client = new LatticeClient({ token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + server.mockEndpoint().get("/api/v1/objects").respondWith().statusCode(400).jsonBody(rawResponseBody).build(); + + await expect(async () => { + return await client.objects.listObjects(); + }).rejects.toThrow( + new Lattice.BadRequestError({ + key: "value", + }), + ); + }); + + test("listObjects (3)", async () => { + const server = mockServerPool.createServer(); + const client = new LatticeClient({ token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + server.mockEndpoint().get("/api/v1/objects").respondWith().statusCode(401).jsonBody(rawResponseBody).build(); + + await expect(async () => { + return await client.objects.listObjects(); + }).rejects.toThrow( + new Lattice.UnauthorizedError({ + key: "value", + }), + ); + }); + + test("listObjects (4)", async () => { + const server = mockServerPool.createServer(); + const client = new LatticeClient({ token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + server.mockEndpoint().get("/api/v1/objects").respondWith().statusCode(500).jsonBody(rawResponseBody).build(); + + await expect(async () => { + return await client.objects.listObjects(); + }).rejects.toThrow( + new Lattice.InternalServerError({ + key: "value", + }), + ); + }); + + test("deleteObject (1)", async () => { const server = mockServerPool.createServer(); const client = new LatticeClient({ token: "test", environment: server.baseUrl }); @@ -16,7 +104,95 @@ describe("Objects", () => { expect(response).toEqual(undefined); }); - test("getObjectMetadata", async () => { + test("deleteObject (2)", async () => { + const server = mockServerPool.createServer(); + const client = new LatticeClient({ token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + server + .mockEndpoint() + .delete("/api/v1/objects/objectPath") + .respondWith() + .statusCode(400) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.objects.deleteObject("objectPath"); + }).rejects.toThrow( + new Lattice.BadRequestError({ + key: "value", + }), + ); + }); + + test("deleteObject (3)", async () => { + const server = mockServerPool.createServer(); + const client = new LatticeClient({ token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + server + .mockEndpoint() + .delete("/api/v1/objects/objectPath") + .respondWith() + .statusCode(401) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.objects.deleteObject("objectPath"); + }).rejects.toThrow( + new Lattice.UnauthorizedError({ + key: "value", + }), + ); + }); + + test("deleteObject (4)", async () => { + const server = mockServerPool.createServer(); + const client = new LatticeClient({ token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + server + .mockEndpoint() + .delete("/api/v1/objects/objectPath") + .respondWith() + .statusCode(404) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.objects.deleteObject("objectPath"); + }).rejects.toThrow( + new Lattice.NotFoundError({ + key: "value", + }), + ); + }); + + test("deleteObject (5)", async () => { + const server = mockServerPool.createServer(); + const client = new LatticeClient({ token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + server + .mockEndpoint() + .delete("/api/v1/objects/objectPath") + .respondWith() + .statusCode(500) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.objects.deleteObject("objectPath"); + }).rejects.toThrow( + new Lattice.InternalServerError({ + key: "value", + }), + ); + }); + + test("getObjectMetadata (1)", async () => { const server = mockServerPool.createServer(); const client = new LatticeClient({ token: "test", environment: server.baseUrl }); @@ -25,4 +201,70 @@ describe("Objects", () => { const headers = await client.objects.getObjectMetadata("objectPath"); expect(headers).toBeInstanceOf(Headers); }); + + test("getObjectMetadata (2)", async () => { + const server = mockServerPool.createServer(); + const client = new LatticeClient({ token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + server + .mockEndpoint() + .head("/api/v1/objects/objectPath") + .respondWith() + .statusCode(400) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.objects.getObjectMetadata("objectPath"); + }).rejects.toThrow( + new Lattice.BadRequestError({ + key: "value", + }), + ); + }); + + test("getObjectMetadata (3)", async () => { + const server = mockServerPool.createServer(); + const client = new LatticeClient({ token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + server + .mockEndpoint() + .head("/api/v1/objects/objectPath") + .respondWith() + .statusCode(401) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.objects.getObjectMetadata("objectPath"); + }).rejects.toThrow( + new Lattice.UnauthorizedError({ + key: "value", + }), + ); + }); + + test("getObjectMetadata (4)", async () => { + const server = mockServerPool.createServer(); + const client = new LatticeClient({ token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + server + .mockEndpoint() + .head("/api/v1/objects/objectPath") + .respondWith() + .statusCode(500) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.objects.getObjectMetadata("objectPath"); + }).rejects.toThrow( + new Lattice.InternalServerError({ + key: "value", + }), + ); + }); }); diff --git a/tests/wire/tasks.test.ts b/tests/wire/tasks.test.ts index 2fbc4f8..ce0b286 100644 --- a/tests/wire/tasks.test.ts +++ b/tests/wire/tasks.test.ts @@ -4,9 +4,10 @@ import { mockServerPool } from "../mock-server/MockServerPool"; import { LatticeClient } from "../../src/Client"; +import * as Lattice from "../../src/api/index"; describe("Tasks", () => { - test("createTask", async () => { + test("createTask (1)", async () => { const server = mockServerPool.createServer(); const client = new LatticeClient({ token: "test", environment: server.baseUrl }); const rawRequestBody = {}; @@ -133,7 +134,89 @@ describe("Tasks", () => { }); }); - test("getTask", async () => { + test("createTask (2)", async () => { + const server = mockServerPool.createServer(); + const client = new LatticeClient({ token: "test", environment: server.baseUrl }); + const rawRequestBody = { + taskId: undefined, + displayName: undefined, + description: undefined, + specification: undefined, + author: undefined, + relations: undefined, + isExecutedElsewhere: undefined, + initialEntities: undefined, + }; + const rawResponseBody = { key: "value" }; + server + .mockEndpoint() + .post("/api/v1/tasks") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(400) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.tasks.createTask({ + taskId: undefined, + displayName: undefined, + description: undefined, + specification: undefined, + author: undefined, + relations: undefined, + isExecutedElsewhere: undefined, + initialEntities: undefined, + }); + }).rejects.toThrow( + new Lattice.BadRequestError({ + key: "value", + }), + ); + }); + + test("createTask (3)", async () => { + const server = mockServerPool.createServer(); + const client = new LatticeClient({ token: "test", environment: server.baseUrl }); + const rawRequestBody = { + taskId: undefined, + displayName: undefined, + description: undefined, + specification: undefined, + author: undefined, + relations: undefined, + isExecutedElsewhere: undefined, + initialEntities: undefined, + }; + const rawResponseBody = { key: "value" }; + server + .mockEndpoint() + .post("/api/v1/tasks") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(401) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.tasks.createTask({ + taskId: undefined, + displayName: undefined, + description: undefined, + specification: undefined, + author: undefined, + relations: undefined, + isExecutedElsewhere: undefined, + initialEntities: undefined, + }); + }).rejects.toThrow( + new Lattice.UnauthorizedError({ + key: "value", + }), + ); + }); + + test("getTask (1)", async () => { const server = mockServerPool.createServer(); const client = new LatticeClient({ token: "test", environment: server.baseUrl }); @@ -259,7 +342,73 @@ describe("Tasks", () => { }); }); - test("updateTaskStatus", async () => { + test("getTask (2)", async () => { + const server = mockServerPool.createServer(); + const client = new LatticeClient({ token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + server + .mockEndpoint() + .get("/api/v1/tasks/taskId") + .respondWith() + .statusCode(400) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.tasks.getTask("taskId"); + }).rejects.toThrow( + new Lattice.BadRequestError({ + key: "value", + }), + ); + }); + + test("getTask (3)", async () => { + const server = mockServerPool.createServer(); + const client = new LatticeClient({ token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + server + .mockEndpoint() + .get("/api/v1/tasks/taskId") + .respondWith() + .statusCode(401) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.tasks.getTask("taskId"); + }).rejects.toThrow( + new Lattice.UnauthorizedError({ + key: "value", + }), + ); + }); + + test("getTask (4)", async () => { + const server = mockServerPool.createServer(); + const client = new LatticeClient({ token: "test", environment: server.baseUrl }); + + const rawResponseBody = { key: "value" }; + server + .mockEndpoint() + .get("/api/v1/tasks/taskId") + .respondWith() + .statusCode(404) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.tasks.getTask("taskId"); + }).rejects.toThrow( + new Lattice.NotFoundError({ + key: "value", + }), + ); + }); + + test("updateTaskStatus (1)", async () => { const server = mockServerPool.createServer(); const client = new LatticeClient({ token: "test", environment: server.baseUrl }); const rawRequestBody = {}; @@ -386,7 +535,88 @@ describe("Tasks", () => { }); }); - test("queryTasks", async () => { + test("updateTaskStatus (2)", async () => { + const server = mockServerPool.createServer(); + const client = new LatticeClient({ token: "test", environment: server.baseUrl }); + const rawRequestBody = { statusVersion: undefined, newStatus: undefined, author: undefined }; + const rawResponseBody = { key: "value" }; + server + .mockEndpoint() + .put("/api/v1/tasks/taskId/status") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(400) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.tasks.updateTaskStatus("taskId", { + statusVersion: undefined, + newStatus: undefined, + author: undefined, + }); + }).rejects.toThrow( + new Lattice.BadRequestError({ + key: "value", + }), + ); + }); + + test("updateTaskStatus (3)", async () => { + const server = mockServerPool.createServer(); + const client = new LatticeClient({ token: "test", environment: server.baseUrl }); + const rawRequestBody = { statusVersion: undefined, newStatus: undefined, author: undefined }; + const rawResponseBody = { key: "value" }; + server + .mockEndpoint() + .put("/api/v1/tasks/taskId/status") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(401) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.tasks.updateTaskStatus("taskId", { + statusVersion: undefined, + newStatus: undefined, + author: undefined, + }); + }).rejects.toThrow( + new Lattice.UnauthorizedError({ + key: "value", + }), + ); + }); + + test("updateTaskStatus (4)", async () => { + const server = mockServerPool.createServer(); + const client = new LatticeClient({ token: "test", environment: server.baseUrl }); + const rawRequestBody = { statusVersion: undefined, newStatus: undefined, author: undefined }; + const rawResponseBody = { key: "value" }; + server + .mockEndpoint() + .put("/api/v1/tasks/taskId/status") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(404) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.tasks.updateTaskStatus("taskId", { + statusVersion: undefined, + newStatus: undefined, + author: undefined, + }); + }).rejects.toThrow( + new Lattice.NotFoundError({ + key: "value", + }), + ); + }); + + test("queryTasks (1)", async () => { const server = mockServerPool.createServer(); const client = new LatticeClient({ token: "test", environment: server.baseUrl }); const rawRequestBody = {}; @@ -430,7 +660,106 @@ describe("Tasks", () => { }); }); - test("listenAsAgent", async () => { + test("queryTasks (2)", async () => { + const server = mockServerPool.createServer(); + const client = new LatticeClient({ token: "test", environment: server.baseUrl }); + const rawRequestBody = { + pageToken: undefined, + parentTaskId: undefined, + statusFilter: undefined, + updateTimeRange: undefined, + }; + const rawResponseBody = { key: "value" }; + server + .mockEndpoint() + .post("/api/v1/tasks/query") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(400) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.tasks.queryTasks({ + pageToken: undefined, + parentTaskId: undefined, + statusFilter: undefined, + updateTimeRange: undefined, + }); + }).rejects.toThrow( + new Lattice.BadRequestError({ + key: "value", + }), + ); + }); + + test("queryTasks (3)", async () => { + const server = mockServerPool.createServer(); + const client = new LatticeClient({ token: "test", environment: server.baseUrl }); + const rawRequestBody = { + pageToken: undefined, + parentTaskId: undefined, + statusFilter: undefined, + updateTimeRange: undefined, + }; + const rawResponseBody = { key: "value" }; + server + .mockEndpoint() + .post("/api/v1/tasks/query") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(401) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.tasks.queryTasks({ + pageToken: undefined, + parentTaskId: undefined, + statusFilter: undefined, + updateTimeRange: undefined, + }); + }).rejects.toThrow( + new Lattice.UnauthorizedError({ + key: "value", + }), + ); + }); + + test("queryTasks (4)", async () => { + const server = mockServerPool.createServer(); + const client = new LatticeClient({ token: "test", environment: server.baseUrl }); + const rawRequestBody = { + pageToken: undefined, + parentTaskId: undefined, + statusFilter: undefined, + updateTimeRange: undefined, + }; + const rawResponseBody = { key: "value" }; + server + .mockEndpoint() + .post("/api/v1/tasks/query") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(404) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.tasks.queryTasks({ + pageToken: undefined, + parentTaskId: undefined, + statusFilter: undefined, + updateTimeRange: undefined, + }); + }).rejects.toThrow( + new Lattice.NotFoundError({ + key: "value", + }), + ); + }); + + test("listenAsAgent (1)", async () => { const server = mockServerPool.createServer(); const client = new LatticeClient({ token: "test", environment: server.baseUrl }); const rawRequestBody = {}; @@ -479,4 +808,54 @@ describe("Tasks", () => { }, }); }); + + test("listenAsAgent (2)", async () => { + const server = mockServerPool.createServer(); + const client = new LatticeClient({ token: "test", environment: server.baseUrl }); + const rawRequestBody = { agentSelector: undefined }; + const rawResponseBody = { key: "value" }; + server + .mockEndpoint() + .post("/api/v1/agent/listen") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(400) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.tasks.listenAsAgent({ + agentSelector: undefined, + }); + }).rejects.toThrow( + new Lattice.BadRequestError({ + key: "value", + }), + ); + }); + + test("listenAsAgent (3)", async () => { + const server = mockServerPool.createServer(); + const client = new LatticeClient({ token: "test", environment: server.baseUrl }); + const rawRequestBody = { agentSelector: undefined }; + const rawResponseBody = { key: "value" }; + server + .mockEndpoint() + .post("/api/v1/agent/listen") + .jsonBody(rawRequestBody) + .respondWith() + .statusCode(401) + .jsonBody(rawResponseBody) + .build(); + + await expect(async () => { + return await client.tasks.listenAsAgent({ + agentSelector: undefined, + }); + }).rejects.toThrow( + new Lattice.UnauthorizedError({ + key: "value", + }), + ); + }); }); diff --git a/yarn.lock b/yarn.lock index 9016e56..8610ae2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,14 +2,6 @@ # yarn lockfile v1 -"@ampproject/remapping@^2.2.0": - version "2.3.0" - resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4" - integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw== - dependencies: - "@jridgewell/gen-mapping" "^0.3.5" - "@jridgewell/trace-mapping" "^0.3.24" - "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.27.1": version "7.27.1" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.27.1.tgz#200f715e66d52a23b221a9435534a91cc13ad5be" @@ -20,25 +12,25 @@ picocolors "^1.1.1" "@babel/compat-data@^7.27.2": - version "7.28.0" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.28.0.tgz#9fc6fd58c2a6a15243cd13983224968392070790" - integrity sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw== + version "7.28.4" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.28.4.tgz#96fdf1af1b8859c8474ab39c295312bfb7c24b04" + integrity sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw== "@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.23.9": - version "7.28.3" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.28.3.tgz#aceddde69c5d1def69b839d09efa3e3ff59c97cb" - integrity sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ== + version "7.28.4" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.28.4.tgz#12a550b8794452df4c8b084f95003bce1742d496" + integrity sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA== dependencies: - "@ampproject/remapping" "^2.2.0" "@babel/code-frame" "^7.27.1" "@babel/generator" "^7.28.3" "@babel/helper-compilation-targets" "^7.27.2" "@babel/helper-module-transforms" "^7.28.3" - "@babel/helpers" "^7.28.3" - "@babel/parser" "^7.28.3" + "@babel/helpers" "^7.28.4" + "@babel/parser" "^7.28.4" "@babel/template" "^7.27.2" - "@babel/traverse" "^7.28.3" - "@babel/types" "^7.28.2" + "@babel/traverse" "^7.28.4" + "@babel/types" "^7.28.4" + "@jridgewell/remapping" "^2.3.5" convert-source-map "^2.0.0" debug "^4.1.0" gensync "^1.0.0-beta.2" @@ -109,20 +101,20 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz#fa52f5b1e7db1ab049445b421c4471303897702f" integrity sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg== -"@babel/helpers@^7.28.3": - version "7.28.3" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.28.3.tgz#b83156c0a2232c133d1b535dd5d3452119c7e441" - integrity sha512-PTNtvUQihsAsDHMOP5pfobP8C6CM4JWXmP8DrEIt46c3r2bf87Ua1zoqevsMo9g+tWDwgWrFP5EIxuBx5RudAw== +"@babel/helpers@^7.28.4": + version "7.28.4" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.28.4.tgz#fe07274742e95bdf7cf1443593eeb8926ab63827" + integrity sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w== dependencies: "@babel/template" "^7.27.2" - "@babel/types" "^7.28.2" + "@babel/types" "^7.28.4" -"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.27.2", "@babel/parser@^7.28.3": - version "7.28.3" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.28.3.tgz#d2d25b814621bca5fe9d172bc93792547e7a2a71" - integrity sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA== +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.27.2", "@babel/parser@^7.28.3", "@babel/parser@^7.28.4": + version "7.28.4" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.28.4.tgz#da25d4643532890932cc03f7705fe19637e03fa8" + integrity sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg== dependencies: - "@babel/types" "^7.28.2" + "@babel/types" "^7.28.4" "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" @@ -252,23 +244,23 @@ "@babel/parser" "^7.27.2" "@babel/types" "^7.27.1" -"@babel/traverse@^7.27.1", "@babel/traverse@^7.28.3": - version "7.28.3" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.28.3.tgz#6911a10795d2cce43ec6a28cffc440cca2593434" - integrity sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ== +"@babel/traverse@^7.27.1", "@babel/traverse@^7.28.3", "@babel/traverse@^7.28.4": + version "7.28.4" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.28.4.tgz#8d456101b96ab175d487249f60680221692b958b" + integrity sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ== dependencies: "@babel/code-frame" "^7.27.1" "@babel/generator" "^7.28.3" "@babel/helper-globals" "^7.28.0" - "@babel/parser" "^7.28.3" + "@babel/parser" "^7.28.4" "@babel/template" "^7.27.2" - "@babel/types" "^7.28.2" + "@babel/types" "^7.28.4" debug "^4.3.1" -"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.27.1", "@babel/types@^7.28.2", "@babel/types@^7.3.3": - version "7.28.2" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.28.2.tgz#da9db0856a9a88e0a13b019881d7513588cf712b" - integrity sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ== +"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.27.1", "@babel/types@^7.28.2", "@babel/types@^7.28.4", "@babel/types@^7.3.3": + version "7.28.4" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.28.4.tgz#0a4e618f4c60a7cd6c11cb2d48060e4dbe38ac3a" + integrity sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q== dependencies: "@babel/helper-string-parser" "^7.27.1" "@babel/helper-validator-identifier" "^7.27.1" @@ -292,30 +284,27 @@ dependencies: statuses "^2.0.1" -"@bundled-es-modules/tough-cookie@^0.1.6": - version "0.1.6" - resolved "https://registry.yarnpkg.com/@bundled-es-modules/tough-cookie/-/tough-cookie-0.1.6.tgz#fa9cd3cedfeecd6783e8b0d378b4a99e52bde5d3" - integrity sha512-dvMHbL464C0zI+Yqxbz6kZ5TOEp7GLW+pry/RWndAR8MJQAXZ2rPmIs8tziTZjeIyhSNZgZbCePtfSbdWqStJw== - dependencies: - "@types/tough-cookie" "^4.0.5" - tough-cookie "^4.1.4" +"@inquirer/ansi@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@inquirer/ansi/-/ansi-1.0.0.tgz#29525c673caf36c12e719712830705b9c31f0462" + integrity sha512-JWaTfCxI1eTmJ1BIv86vUfjVatOdxwD0DAVKYevY8SazeUUZtW+tNbsdejVO1GYE0GXJW1N1ahmiC3TFd+7wZA== "@inquirer/confirm@^5.0.0": - version "5.1.15" - resolved "https://registry.yarnpkg.com/@inquirer/confirm/-/confirm-5.1.15.tgz#c502d5c642fdba0669b17442b40794c97bdbccb4" - integrity sha512-SwHMGa8Z47LawQN0rog0sT+6JpiL0B7eW9p1Bb7iCeKDGTI5Ez25TSc2l8kw52VV7hA4sX/C78CGkMrKXfuspA== + version "5.1.18" + resolved "https://registry.yarnpkg.com/@inquirer/confirm/-/confirm-5.1.18.tgz#0b76e5082d834c0e3528023705b867fc1222d535" + integrity sha512-MilmWOzHa3Ks11tzvuAmFoAd/wRuaP3SwlT1IZhyMke31FKLxPiuDWcGXhU+PKveNOpAc4axzAgrgxuIJJRmLw== dependencies: - "@inquirer/core" "^10.1.15" + "@inquirer/core" "^10.2.2" "@inquirer/type" "^3.0.8" -"@inquirer/core@^10.1.15": - version "10.1.15" - resolved "https://registry.yarnpkg.com/@inquirer/core/-/core-10.1.15.tgz#8feb69fd536786181a2b6bfb84d8674faa9d2e59" - integrity sha512-8xrp836RZvKkpNbVvgWUlxjT4CraKk2q+I3Ksy+seI2zkcE+y6wNs1BVhgcv8VyImFecUhdQrYLdW32pAjwBdA== +"@inquirer/core@^10.2.2": + version "10.2.2" + resolved "https://registry.yarnpkg.com/@inquirer/core/-/core-10.2.2.tgz#d31eb50ba0c76b26e7703c2c0d1d0518144c23ab" + integrity sha512-yXq/4QUnk4sHMtmbd7irwiepjB8jXU0kkFRL4nr/aDBA2mDz13cMakEWdDwX3eSCTkk03kwcndD1zfRAIlELxA== dependencies: + "@inquirer/ansi" "^1.0.0" "@inquirer/figures" "^1.0.13" "@inquirer/type" "^3.0.8" - ansi-escapes "^4.3.2" cli-width "^4.1.0" mute-stream "^2.0.0" signal-exit "^4.1.0" @@ -548,6 +537,14 @@ "@jridgewell/sourcemap-codec" "^1.5.0" "@jridgewell/trace-mapping" "^0.3.24" +"@jridgewell/remapping@^2.3.5": + version "2.3.5" + resolved "https://registry.yarnpkg.com/@jridgewell/remapping/-/remapping-2.3.5.tgz#375c476d1972947851ba1e15ae8f123047445aa1" + integrity sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ== + dependencies: + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.24" + "@jridgewell/resolve-uri@^3.1.0": version "3.1.2" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" @@ -567,9 +564,9 @@ integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== "@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25", "@jridgewell/trace-mapping@^0.3.28": - version "0.3.30" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz#4a76c4daeee5df09f5d3940e087442fb36ce2b99" - integrity sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q== + version "0.3.31" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz#db15d6781c931f3a251a3dac39501c98a6082fd0" + integrity sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw== dependencies: "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" @@ -736,16 +733,16 @@ integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== "@types/node@*": - version "24.3.0" - resolved "https://registry.yarnpkg.com/@types/node/-/node-24.3.0.tgz#89b09f45cb9a8ee69466f18ee5864e4c3eb84dec" - integrity sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow== + version "24.5.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-24.5.0.tgz#70a482e6b1d50e603729d74e62a9a43705ddc9d7" + integrity sha512-y1dMvuvJspJiPSDZUQ+WMBvF7dpnEqN4x9DDC9ie5Fs/HUZJA3wFp7EhHoVaKX/iI0cRoECV8X2jL8zi0xrHCg== dependencies: - undici-types "~7.10.0" + undici-types "~7.12.0" "@types/node@^18.19.70": - version "18.19.123" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.123.tgz#08a3e4f5e0c73b8840c677b7635ce59d5dc1f76d" - integrity sha512-K7DIaHnh0mzVxreCR9qwgNxp3MH9dltPNIEddW9MYUlcKAzm+3grKNSTe2vCJHI1FaLpvpL5JGJrz1UZDKYvDg== + version "18.19.125" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.125.tgz#179a577373347f29560cc4dfef9ee36229a09647" + integrity sha512-4TWNu0IxTQcszliYdW2mxrVvhHeERUeDCUwVuvQFn9JCU02kxrUDs8v52yOazPo7wLHKgqEd2FKxlSN6m8Deqg== dependencies: undici-types "~5.26.4" @@ -759,7 +756,7 @@ resolved "https://registry.yarnpkg.com/@types/statuses/-/statuses-2.0.6.tgz#66748315cc9a96d63403baa8671b2c124f8633aa" integrity sha512-xMAgYwceFhRA2zY+XbEA7mxYbA093wdiW8Vu6gZPGWy9cmOyU9XesH1tNcEWsKFd5Vzrqx5T3D38PWx1FIIXkA== -"@types/tough-cookie@*", "@types/tough-cookie@^4.0.5": +"@types/tough-cookie@*": version "4.0.5" resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.5.tgz#cb6e2a691b70cb177c6e3ae9c1d2e8b2ea8cd304" integrity sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA== @@ -932,7 +929,7 @@ acorn-walk@^8.0.2: dependencies: acorn "^8.11.0" -acorn@^8.1.0, acorn@^8.11.0, acorn@^8.14.0, acorn@^8.15.0, acorn@^8.8.1: +acorn@^8.1.0, acorn@^8.11.0, acorn@^8.15.0, acorn@^8.8.1: version "8.15.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.15.0.tgz#a360898bc415edaac46c8241f6383975b930b816" integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg== @@ -968,7 +965,7 @@ ajv@^8.0.0, ajv@^8.9.0: json-schema-traverse "^1.0.0" require-from-string "^2.0.2" -ansi-escapes@^4.2.1, ansi-escapes@^4.3.2: +ansi-escapes@^4.2.1: version "4.3.2" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== @@ -1080,6 +1077,11 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +baseline-browser-mapping@^2.8.2: + version "2.8.4" + resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.8.4.tgz#e553e12272c4965682743705efd8b4b4cf0d709b" + integrity sha512-L+YvJwGAgwJBV1p6ffpSTa2KRc69EeeYGYjRVWKs0GKrK+LON0GC0gV+rKSNtALEDvMDqkvCFq9r1r94/Gjwxw== + brace-expansion@^1.1.7: version "1.1.12" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.12.tgz#ab9b454466e5a8cc3a187beaad580412a9c5b843" @@ -1096,13 +1098,14 @@ braces@^3.0.3: fill-range "^7.1.1" browserslist@^4.24.0: - version "4.25.2" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.25.2.tgz#90c1507143742d743544ae6e92bca3348adff667" - integrity sha512-0si2SJK3ooGzIawRu61ZdPCO1IncZwS8IzuX73sPZsXW6EQ/w/DAfPyKI8l1ETTCr2MnvqWitmlCUxgdul45jA== - dependencies: - caniuse-lite "^1.0.30001733" - electron-to-chromium "^1.5.199" - node-releases "^2.0.19" + version "4.26.0" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.26.0.tgz#035ca84b4ff312a3c6a7014a77beb83456a882dd" + integrity sha512-P9go2WrP9FiPwLv3zqRD/Uoxo0RSHjzFCiQz7d4vbmwNqQFo9T9WCeP/Qn5EbcKQY6DBbkxEXNcpJOmncNrb7A== + dependencies: + baseline-browser-mapping "^2.8.2" + caniuse-lite "^1.0.30001741" + electron-to-chromium "^1.5.218" + node-releases "^2.0.21" update-browserslist-db "^1.1.3" bs-logger@^0.2.6: @@ -1147,10 +1150,10 @@ camelcase@^6.2.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-lite@^1.0.30001733: - version "1.0.30001735" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001735.tgz#ba658fd3fd24a4106fd68d5ce472a2c251494dbe" - integrity sha512-EV/laoX7Wq2J9TQlyIXRxTJqIw4sxfXS4OYgudGxBYRuTv0q7AM6yMEpU/Vo1I94thg9U6EZ2NfZx9GJq83u7w== +caniuse-lite@^1.0.30001741: + version "1.0.30001741" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001741.tgz#67fb92953edc536442f3c9da74320774aa523143" + integrity sha512-QGUGitqsc8ARjLdgAfxETDhRbJ0REsP6O3I96TAth/mVjh2cYzN2u+3AzPP3aVSm2FehEItaJw1xd+IGBXWeSw== chalk@^4.0.0, chalk@^4.1.0: version "4.1.2" @@ -1292,9 +1295,9 @@ data-urls@^3.0.2: whatwg-url "^11.0.0" debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: - version "4.4.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.1.tgz#e5a8bc6cbc4c6cd3e64308b0693a3d4fa550189b" - integrity sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ== + version "4.4.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" + integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== dependencies: ms "^2.1.3" @@ -1304,9 +1307,9 @@ decimal.js@^10.4.2: integrity sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg== dedent@^1.0.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.6.0.tgz#79d52d6389b1ffa67d2bcef59ba51847a9d503b2" - integrity sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA== + version "1.7.0" + resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.7.0.tgz#c1f9445335f0175a96587be245a282ff451446ca" + integrity sha512-HGFtf8yhuhGhqO07SV79tRp+br4MnbdjeVxotpn1QBl30pcLLCQjX5b2295ll0fv8RKDKsmWYrl05usHM9CewQ== deepmerge@^4.2.2: version "4.3.1" @@ -1344,10 +1347,10 @@ dunder-proto@^1.0.1: es-errors "^1.3.0" gopd "^1.2.0" -electron-to-chromium@^1.5.199: - version "1.5.203" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.203.tgz#ef7fc2f7e1b816fa4535c861d1ec1348204142b6" - integrity sha512-uz4i0vLhfm6dLZWbz/iH88KNDV+ivj5+2SA+utpgjKaj9Q0iDLuwk6Idhe9BTxciHudyx6IvTvijhkPvFGUQ0g== +electron-to-chromium@^1.5.218: + version "1.5.218" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.218.tgz#921042a011a98a4620853c9d391ab62bcc124400" + integrity sha512-uwwdN0TUHs8u6iRgN8vKeWZMRll4gBkz+QMqdS7DDe49uiK68/UX92lFb61oiFPrpYZNeZIqa4bA7O6Aiasnzg== emittery@^0.13.1: version "0.13.1" @@ -1373,9 +1376,9 @@ entities@^6.0.0: integrity sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g== error-ex@^1.3.1: - version "1.3.2" - resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" - integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + version "1.3.4" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.4.tgz#b3a8d8bb6f92eecc1629e3e27d3c8607a8a32414" + integrity sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ== dependencies: is-arrayish "^0.2.1" @@ -1514,9 +1517,9 @@ fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.1.0: integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== fast-uri@^3.0.1: - version "3.0.6" - resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.0.6.tgz#88f130b77cfaea2378d56bf970dea21257a68748" - integrity sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw== + version "3.1.0" + resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.1.0.tgz#66eecff6c764c0df9b762e62ca7edcfb53b4edfa" + integrity sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA== fb-watchman@^2.0.0: version "2.0.2" @@ -2407,13 +2410,12 @@ ms@^2.1.3: integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== msw@^2.8.4: - version "2.10.5" - resolved "https://registry.yarnpkg.com/msw/-/msw-2.10.5.tgz#3e43f12e97581c260bf38d8817732b9fec3bfdb0" - integrity sha512-0EsQCrCI1HbhpBWd89DvmxY6plmvrM96b0sCIztnvcNHQbXn5vqwm1KlXslo6u4wN9LFGLC1WFjjgljcQhe40A== + version "2.11.2" + resolved "https://registry.yarnpkg.com/msw/-/msw-2.11.2.tgz#622d83855f456a5f93b1528f6eb6f4c0114623c3" + integrity sha512-MI54hLCsrMwiflkcqlgYYNJJddY5/+S0SnONvhv1owOplvqohKSQyGejpNdUGyCwgs4IH7PqaNbPw/sKOEze9Q== dependencies: "@bundled-es-modules/cookie" "^2.0.1" "@bundled-es-modules/statuses" "^1.0.1" - "@bundled-es-modules/tough-cookie" "^0.1.6" "@inquirer/confirm" "^5.0.0" "@mswjs/interceptors" "^0.39.1" "@open-draft/deferred-promise" "^2.2.0" @@ -2426,7 +2428,9 @@ msw@^2.8.4: outvariant "^1.4.3" path-to-regexp "^6.3.0" picocolors "^1.1.1" + rettime "^0.7.0" strict-event-emitter "^0.5.1" + tough-cookie "^6.0.0" type-fest "^4.26.1" yargs "^17.7.2" @@ -2450,10 +2454,10 @@ node-int64@^0.4.0: resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== -node-releases@^2.0.19: - version "2.0.19" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.19.tgz#9e445a52950951ec4d177d843af370b411caf314" - integrity sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw== +node-releases@^2.0.21: + version "2.0.21" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.21.tgz#f59b018bc0048044be2d4c4c04e4c8b18160894c" + integrity sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw== normalize-path@^3.0.0: version "3.0.0" @@ -2468,9 +2472,9 @@ npm-run-path@^4.0.1: path-key "^3.0.0" nwsapi@^2.2.2: - version "2.2.21" - resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.21.tgz#8df7797079350adda208910d8c33fc4c2d7520c3" - integrity sha512-o6nIY3qwiSXl7/LuOU0Dmuctd34Yay0yeuZRLFmDPrrdHpXKFndPj3hM+YEPVHYC5fx2otBx4Ilc/gyYSAUaIA== + version "2.2.22" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.22.tgz#109f9530cda6c156d6a713cdf5939e9f0de98b9d" + integrity sha512-ujSMe1OWVn55euT1ihwCI1ZcAaAU3nxUiDwfDQldc51ZXaB9m2AyOn6/jh1BLe2t/G8xd6uKG1UBF2aZJeg2SQ== once@^1.3.0: version "1.4.0" @@ -2678,6 +2682,11 @@ resolve@^1.20.0: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +rettime@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/rettime/-/rettime-0.7.0.tgz#c040f1a65e396eaa4b8346dd96ed937edc79d96f" + integrity sha512-LPRKoHnLKd/r3dVxcwO7vhCW+orkOGj9ViueosEBK6ie89CijnfRlhaDhHq/3Hxu4CkWQtxwlBG0mzTQY6uQjw== + safe-buffer@^5.1.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" @@ -2866,9 +2875,9 @@ symbol-tree@^3.2.4: integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== tapable@^2.1.1, tapable@^2.2.0: - version "2.2.2" - resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.2.tgz#ab4984340d30cb9989a490032f086dbb8b56d872" - integrity sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg== + version "2.2.3" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.3.tgz#4b67b635b2d97578a06a2713d2f04800c237e99b" + integrity sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg== terser-webpack-plugin@^5.3.11: version "5.3.14" @@ -2882,12 +2891,12 @@ terser-webpack-plugin@^5.3.11: terser "^5.31.1" terser@^5.31.1: - version "5.43.1" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.43.1.tgz#88387f4f9794ff1a29e7ad61fb2932e25b4fdb6d" - integrity sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg== + version "5.44.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.44.0.tgz#ebefb8e5b8579d93111bfdfc39d2cf63879f4a82" + integrity sha512-nIVck8DK+GM/0Frwd+nIhZ84pR/BX7rmXMfYwyg+Sri5oGVE99/E3KvXqpC2xHFxyqXyGHTKBSioxxplrO4I4w== dependencies: "@jridgewell/source-map" "^0.3.3" - acorn "^8.14.0" + acorn "^8.15.0" commander "^2.20.0" source-map-support "~0.5.20" @@ -2900,6 +2909,18 @@ test-exclude@^6.0.0: glob "^7.1.4" minimatch "^3.0.4" +tldts-core@^7.0.14: + version "7.0.14" + resolved "https://registry.yarnpkg.com/tldts-core/-/tldts-core-7.0.14.tgz#eb49edf8a39a37a2372ffc22f82d6ac725ace6cd" + integrity sha512-viZGNK6+NdluOJWwTO9olaugx0bkKhscIdriQQ+lNNhwitIKvb+SvhbYgnCz6j9p7dX3cJntt4agQAKMXLjJ5g== + +tldts@^7.0.5: + version "7.0.14" + resolved "https://registry.yarnpkg.com/tldts/-/tldts-7.0.14.tgz#5dc352e087c12978b7d1d36d8a346496e04dca72" + integrity sha512-lMNHE4aSI3LlkMUMicTmAG3tkkitjOQGDTFboPJwAg2kJXKP1ryWEyqujktg5qhrFZOkk5YFzgkxg3jErE+i5w== + dependencies: + tldts-core "^7.0.14" + tmpl@1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" @@ -2912,7 +2933,7 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -tough-cookie@^4.1.2, tough-cookie@^4.1.4: +tough-cookie@^4.1.2: version "4.1.4" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.4.tgz#945f1461b45b5a8c76821c33ea49c3ac192c1b36" integrity sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag== @@ -2922,6 +2943,13 @@ tough-cookie@^4.1.2, tough-cookie@^4.1.4: universalify "^0.2.0" url-parse "^1.5.3" +tough-cookie@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-6.0.0.tgz#11e418b7864a2c0d874702bc8ce0f011261940e5" + integrity sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w== + dependencies: + tldts "^7.0.5" + tr46@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/tr46/-/tr46-3.0.0.tgz#555c4e297a950617e8eeddef633c87d4d9d6cbf9" @@ -2930,9 +2958,9 @@ tr46@^3.0.0: punycode "^2.1.1" ts-jest@^29.3.4: - version "29.4.1" - resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.4.1.tgz#42d33beb74657751d315efb9a871fe99e3b9b519" - integrity sha512-SaeUtjfpg9Uqu8IbeDKtdaS0g8lS6FT6OzM3ezrDfErPJPHNDo/Ey+VFGP1bQIDfagYDLyRpd7O15XpG1Es2Uw== + version "29.4.2" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.4.2.tgz#095fe8770df81f2e39f2c34a6bcafac58b834423" + integrity sha512-pBNOkn4HtuLpNrXTMVRC9b642CBaDnKqWXny4OzuoULT9S7Kf8MMlaRe2veKax12rjf5WcpMBhVPbQurlWGNxA== dependencies: bs-logger "^0.2.6" fast-json-stable-stringify "^2.1.0" @@ -2945,9 +2973,9 @@ ts-jest@^29.3.4: yargs-parser "^21.1.1" ts-loader@^9.5.1: - version "9.5.2" - resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-9.5.2.tgz#1f3d7f4bb709b487aaa260e8f19b301635d08020" - integrity sha512-Qo4piXvOTWcMGIgRiuFa6nHNm+54HbYaZCKqc9eeZCLRy3XqafQgwX2F7mofrbJG3g7EEb+lkiR+z2Lic2s3Zw== + version "9.5.4" + resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-9.5.4.tgz#44b571165c10fb5a90744aa5b7e119233c4f4585" + integrity sha512-nCz0rEwunlTZiy6rXFByQU1kVVpCIgUpc/psFiKVrUwrizdnIbRFu8w7bxhUF0X613DYwT4XzrZHpVyMe758hQ== dependencies: chalk "^4.1.0" enhanced-resolve "^5.0.0" @@ -2985,10 +3013,10 @@ undici-types@~5.26.4: resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== -undici-types@~7.10.0: - version "7.10.0" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.10.0.tgz#4ac2e058ce56b462b056e629cc6a02393d3ff350" - integrity sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag== +undici-types@~7.12.0: + version "7.12.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.12.0.tgz#15c5c7475c2a3ba30659529f5cdb4674b622fafb" + integrity sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ== universalify@^0.2.0: version "0.2.0" @@ -3195,6 +3223,6 @@ yocto-queue@^0.1.0: integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== yoctocolors-cjs@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz#f4b905a840a37506813a7acaa28febe97767a242" - integrity sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA== + version "2.1.3" + resolved "https://registry.yarnpkg.com/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz#7e4964ea8ec422b7a40ac917d3a344cfd2304baa" + integrity sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==