From 8a0f5a0a215f1e4ff318e8b4a81405f6851f4c12 Mon Sep 17 00:00:00 2001 From: fern-api <115122769+fern-api[bot]@users.noreply.github.com> Date: Tue, 16 Sep 2025 22:16:22 +0000 Subject: [PATCH] SDK regeneration --- .npmignore | 3 +- README.md | 25 +- 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 | 75 ++- .../client/requests/GetObjectRequest.ts | 8 +- .../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, 1770 insertions(+), 275 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..e7f3acf 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,10 @@ ![](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 Anduril TypeScript library provides convenient access to the Anduril 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 @@ -626,3 +639,13 @@ const client = new LatticeClient({ fetcher: // provide your implementation here }); ``` + +## Contributing + +While we value open-source contributions to this SDK, this library is generated programmatically. +Additions made directly to this library would have to be moved over to our generation code, +otherwise they would be overwritten upon the next generated release. Feel free to open a PR as +a proof of concept, but know that we will not be able to merge it as-is. We suggest opening +an issue first to discuss with us! + +On the other hand, contributions to the README are always very welcome! 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..67195e5 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, @@ -166,6 +169,14 @@ export class Objects { requestOptions?: Objects.RequestOptions, ): Promise> { const { "Accept-Encoding": acceptEncoding } = request; + let _headers: core.Fetcher.Args["headers"] = mergeHeaders( + this._options?.headers, + mergeOnlyDefinedHeaders({ + Authorization: await this._getAuthorizationHeader(), + "Accept-Encoding": acceptEncoding != null ? acceptEncoding : undefined, + }), + requestOptions?.headers, + ); const _response = await core.fetcher({ url: core.url.join( (await core.Supplier.get(this._options.baseUrl)) ?? @@ -174,14 +185,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 +259,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 +273,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 +349,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 +362,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 +434,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 +447,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..fde7950 100644 --- a/src/api/resources/objects/client/requests/GetObjectRequest.ts +++ b/src/api/resources/objects/client/requests/GetObjectRequest.ts @@ -4,9 +4,11 @@ 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; } 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==