From d8039abd6c7c0468d42ce6caf054873318608c01 Mon Sep 17 00:00:00 2001 From: toptobes Date: Tue, 9 Apr 2024 02:37:49 -0500 Subject: [PATCH] a whole lot more documentation --- .env.example | 2 +- .np-config.json | 4 + DEVGUIDE.md | 51 ++-- etc/astra-db-ts.api.md | 268 +++++++----------- package-lock.json | 106 +++++-- package.json | 24 +- scripts/build.sh | 2 +- src/api/data-api-http-client.ts | 8 +- src/client/data-api-client.ts | 15 + src/data-api/collection.ts | 9 +- src/data-api/cursor.ts | 4 +- src/data-api/errors.ts | 101 ++++++- src/data-api/events.ts | 15 + src/data-api/ids.ts | 28 +- src/data-api/index.ts | 2 +- src/data-api/types/filter.ts | 50 +++- src/data-api/types/insert/insert-many.ts | 3 +- src/data-api/types/misc/bulk-write.ts | 147 +++++++++- src/data-api/types/update-filter.ts | 78 +++-- src/devops/errors.ts | 122 ++++++-- src/devops/types/admin/admin-common.ts | 8 + src/devops/types/admin/create-database.ts | 5 + src/devops/types/admin/list-databases.ts | 4 + src/version.ts | 4 +- tests/fixtures.ts | 2 + .../data-api/collection/misc.test.ts | 27 ++ .../data-api/collection/options.test.ts | 5 - tests/integration/data-api/cursor.test.ts | 4 +- tests/integration/data-api/db.test.ts | 3 +- tests/typing/collections/find/find-one.ts | 12 +- tests/typing/strict-filter.ts | 13 +- tests/typing/strict-update-filter.ts | 4 +- tests/unit/data-api/cursor.test.ts | 16 +- tests/unit/data-api/errors.test.ts | 2 +- tests/unit/data-api/ids.test.ts | 66 +---- tests/unit/devops/errors.test.ts | 12 +- 36 files changed, 804 insertions(+), 422 deletions(-) create mode 100644 .np-config.json diff --git a/.env.example b/.env.example index b4b56e41..92c2ce8b 100644 --- a/.env.example +++ b/.env.example @@ -8,7 +8,7 @@ APPLICATION_TOKEN=AstraCS: ASTRA_RUN_VECTORIZE_TESTS= # Set this to some value to enable running long-running tests -ASTRA_RUN_LONG_TESTS= +ASTRA_RUN_LONG_TESTS`= # Set this to some value to enable running admin tests ASTRA_RUN_ADMIN_TESTS= diff --git a/.np-config.json b/.np-config.json new file mode 100644 index 00000000..684546ad --- /dev/null +++ b/.np-config.json @@ -0,0 +1,4 @@ +{ + "testScript": "test:types", + "anyBranch": true +} diff --git a/DEVGUIDE.md b/DEVGUIDE.md index 9205dc94..360ddc4f 100644 --- a/DEVGUIDE.md +++ b/DEVGUIDE.md @@ -1,7 +1,7 @@ # Contents 1. [Running the tests](#running-the-tests) 2. [Linting](#linting) -3. [Building API Reference Documentation](#building-api-reference-documentation) +3. [Building the library](#building-the-library) ## Running the tests Prerequisites: @@ -14,13 +14,10 @@ Prerequisites: npm run test # Run only unit tests -npm run test:unit +npm run test -- -f 'unit.' # Run only integration tests -npm run test:integration - -# Run both unit and integration tests with coverage check -npm run test:coverage +npm run test -- -f 'integration.' # Run tsc with the noEmit flag to check for type errors npm run test:types @@ -31,10 +28,12 @@ Tests can be given certain tags to allow for more granular control over which te - `[long]`/`'LONG'`: Longer running tests that take more than a few seconds to run - `[admin]`/`'ADMIN'`: Tests that require admin permissions to run - `[dev]`/`'DEV'`: Tests that require the dev environment to run +- `[prod]`/`'PROD'`: Tests that require the dev environment to run +- `[vectorize]`/`'VECTORIZE'`: Tests that require a specific vectorize-enabled kube to run -To enable these tags, you can set the corresponding environment variables to some values. The env variables are in the -`env.example` file, but they're repeated here for convenience: -- `ASTRA_RUN_DEV_TESTS` +To enable these some of these tags, you can set the corresponding environment variables to some values. The env +variables are in the `env.example` file, but they're repeated here for convenience: +- `ASTRA_RUN_VECTORIZE_TESTS` - `ASTRA_RUN_LONG_TESTS` - `ASTRA_RUN_ADMIN_TESTS` @@ -43,6 +42,13 @@ Or you can run the tests by doing something like env ASTRA_RUN_LONG_TESTS=1 npm run test ``` +The `PROD` and `DEV` tags are enabled/disabled automatically, inferred from the astra endpoint URL. + +Use the following to run tests with ADMIN and LONG tags automatically enabled (note that doesn't include vectorize tests): +```shell +npm run test:all +``` + ### Adding your own tagged tests To enforce the tags, use the `assertTestsEnabled` function from `test/fixtures.ts`, which will skip the function if the given tag is not enabled. @@ -65,10 +71,18 @@ If a new tag really, really, needs to be added, it can be done by adding a new e format, and updating the `assertTestsEnabled` function. However, this should be done sparingly, as it can make the test suite harder to manage. +### Coverage testing + +To run coverage testing, run the following command: +```shell +npm run test:coverage +``` + +This uses `test:all` under the hood, as well as a "bail early" flag as there's not really a point continuing to run +tests if one of them fails, as the coverage report will be impacted. + ## Linting -Run `npm run lint` to run ESLint. -ESLint will point out any formatting and code quality issues it finds. -You should try to run `npm run lint` before committing to minimize risk of regressions. +Run `npm run lint` to run ESLint. ESLint will point out any formatting and code quality issues it finds. ## Building the library At the moment, you need to be using a unix-like system to build the library, as it uses a small shell script, @@ -80,16 +94,3 @@ To build it, just run `npm run build`, which does the following: - Runs `tsc` to compile the TypeScript files & resolves path aliases w/ `tsc-alias` - Uses `api-extractor` to generate the API report & generate a rolled-up `.d.ts` file - Deletes any extraneous `.d.ts` files - -## Building API Reference Documentation -API Documentation of this library is generated using [jsdoc-to-markdown](https://github.com/jsdoc2md/jsdoc-to-markdown) - -Run the following to generate API documentation. This takes the `APIReference.hbs` and the library code as input and generates and `APIReference.md` file. - -```shell -# Generate API documentation -npm run build:docs - -# Optionally serve docs locally -npx markserv APIReference.md -``` diff --git a/etc/astra-db-ts.api.md b/etc/astra-db-ts.api.md index afa763de..8f0e6d3d 100644 --- a/etc/astra-db-ts.api.md +++ b/etc/astra-db-ts.api.md @@ -70,7 +70,7 @@ export interface AdminSpawnOptions { monitorCommands?: boolean; } -// @public (undocumented) +// @public export type AnyBulkWriteOperation = { insertOne: InsertOneModel; } | { @@ -87,15 +87,13 @@ export type AnyBulkWriteOperation = { // @public export interface ArrayFilterOps { - // (undocumented) $all?: Elem; - // (undocumented) $size?: number; } // Warning: (ae-forgotten-export) The symbol "PickArrayTypes" needs to be exported by the entry point index.d.ts // -// @public (undocumented) +// @public export type ArrayUpdate = { [K in keyof Schema as any[] extends Schema[K] ? K : never]?: PickArrayTypes; }; @@ -132,9 +130,7 @@ export class AstraDbAdmin extends DbAdmin { // @public export class BulkWriteError extends CumulativeDataAPIError { - // (undocumented) name: string; - // (undocumented) readonly partialResult: BulkWriteResult; } @@ -146,24 +142,22 @@ export interface BulkWriteOrderedOptions extends WithTimeout { ordered: true; } -// @public (undocumented) +// @public export class BulkWriteResult { - constructor(deletedCount?: number, insertedCount?: number, matchedCount?: number, modifiedCount?: number, upsertedCount?: number, upsertedIds?: Record>, _raw?: object[]); - // (undocumented) + constructor( + deletedCount?: number, + insertedCount?: number, + matchedCount?: number, + modifiedCount?: number, + upsertedCount?: number, + upsertedIds?: Record>, _raw?: object[]); readonly deletedCount: number; - // (undocumented) getRawResponse(): Record[]; - // (undocumented) - getUpsertedIdAt(index: number): IdOf; - // (undocumented) + getUpsertedIdAt(index: number): IdOf | undefined; readonly insertedCount: number; - // (undocumented) readonly matchedCount: number; - // (undocumented) readonly modifiedCount: number; - // (undocumented) readonly upsertedCount: number; - // (undocumented) readonly upsertedIds: Record>; } @@ -188,10 +182,10 @@ export class Collection { deleteAll(options?: WithTimeout): Promise; deleteMany(filter?: Filter, options?: WithTimeout): Promise; deleteOne(filter?: Filter, options?: DeleteOneOptions): Promise; - distinct(key: Key, filter?: Filter): Promise>)[Key]>[]>; + distinct(key: Key, filter?: Filter): Promise>)[Key]>[]>; drop(options?: WithTimeout): Promise; - find(filter: Filter, options?: FindOptions): FindCursor, FoundDoc>; - findOne(filter: Filter, options?: FindOneOptions): Promise | null>; + find(filter: Filter, options?: FindOptions): FindCursor, FoundDoc>; + findOne(filter: Filter, options?: FindOneOptions): Promise | null>; findOneAndDelete(filter: Filter, options: FindOneAndDeleteOptions & { includeResultMetadata: true; }): Promise>; @@ -219,12 +213,10 @@ export class Collection { updateOne(filter: Filter, update: UpdateFilter, options?: UpdateOneOptions): Promise>; } -// @public (undocumented) +// @public export class CollectionAlreadyExistsError extends DataAPIError { constructor(namespace: string, collectionName: string); - // (undocumented) readonly collectionName: string; - // (undocumented) readonly namespace: string; } @@ -276,7 +268,7 @@ export interface CreateCollectionOptions extends WithTim checkExists?: boolean; } -// @public (undocumented) +// @public export type CreateDatabaseOptions = AdminBlockingOptions & { dbOptions?: DbSpawnOptions; }; @@ -286,7 +278,7 @@ export abstract class CumulativeDataAPIError extends DataAPIResponseError { readonly partialResult: unknown; } -// @public (undocumented) +// @public export type CurrentDate = { [K in keyof Schema as Schema[K] extends Date | { $date: number; @@ -294,7 +286,7 @@ export type CurrentDate = { }; // @public -export class CursorAlreadyInitializedError extends DataAPIError { +export class CursorIsStartedError extends DataAPIError { constructor(message: string); } @@ -306,10 +298,10 @@ export class DataAPIClient extends DataAPIClientEventEmitterBase { db(id: string, region: string, options?: DbSpawnOptions): Db; } -// @public (undocumented) +// @public export const DataAPIClientEventEmitterBase: new () => TypedEmitter; -// @public (undocumented) +// @public export type DataAPIClientEvents = DataAPICommandEvents & AdminCommandEvents; // @public @@ -352,20 +344,19 @@ export class DataAPIResponseError extends DataAPIError { readonly message: string; } -// @public (undocumented) +// @public export class DataAPITimeout extends DataAPIError { constructor(timeout: number); - // (undocumented) readonly timeout: number; } -// @public (undocumented) +// @public export type DatabaseAction = 'park' | 'unpark' | 'resize' | 'resetPassword' | 'addKeyspace' | 'addDatacenters' | 'terminateDatacenter' | 'getCreds' | 'terminate' | 'removeKeyspace' | 'removeMigrationProxy' | 'launchMigrationProxy'; -// @public (undocumented) +// @public export type DatabaseCloudProvider = 'AWS' | 'GCP' | 'AZURE'; -// @public (undocumented) +// @public export type DatabaseCloudProviderFilter = DatabaseCloudProvider | 'ALL'; // @public @@ -390,10 +381,10 @@ export interface DatabaseInfo { user: string; } -// @public (undocumented) +// @public export type DatabaseStatus = 'ACTIVE' | 'PENDING' | 'PREPARING' | 'PREPARED' | 'INITIALIZING' | 'PARKED' | 'PARKING' | 'UNPARKING' | 'TERMINATED' | 'TERMINATING' | 'RESIZING' | 'ERROR' | 'MAINTENANCE' | 'SUSPENDED' | 'UNKNOWN'; -// @public (undocumented) +// @public export type DatabaseStatusFilter = DatabaseStatus | 'ALL' | 'NONTERMINATED'; // @public @@ -404,34 +395,20 @@ export interface DatabaseStorageInfo { usedStorage?: number; } -// @public (undocumented) +// @public export type DatabaseTier = 'developer' | 'A5' | 'A10' | 'A20' | 'A40' | 'C10' | 'C20' | 'C40' | 'D10' | 'D20' | 'D40' | 'serverless'; // @public export interface DateFilterOps { - // (undocumented) - $date?: number; - // (undocumented) - $gt?: Date | { - $date: number; - }; - // (undocumented) - $gte?: Date | { - $date: number; - }; - // (undocumented) - $lt?: Date | { - $date: number; - }; - // (undocumented) - $lte?: Date | { - $date: number; - }; + $gt?: Date; + $gte?: Date; + $lt?: Date; + $lte?: Date; } // Warning: (ae-forgotten-export) The symbol "ContainsDate" needs to be exported by the entry point index.d.ts // -// @public (undocumented) +// @public export type DateUpdate = { [K in keyof Schema as ContainsDate extends true ? K : never]?: Date | { $date: number; @@ -491,15 +468,12 @@ export interface DefaultIdOptions { // @public export class DeleteManyError extends CumulativeDataAPIError { - // (undocumented) name: string; - // (undocumented) readonly partialResult: DeleteManyResult; } -// @public (undocumented) +// @public export interface DeleteManyModel { - // (undocumented) filter: Filter; } @@ -508,9 +482,8 @@ export interface DeleteManyResult { deletedCount: number; } -// @public (undocumented) +// @public export interface DeleteOneModel { - // (undocumented) filter: Filter; } @@ -527,44 +500,37 @@ export interface DeleteOneResult { deletedCount: 0 | 1; } -// @public (undocumented) +// @public export abstract class DevOpsAPIError extends Error { } -// @public (undocumented) +// @public export interface DevOpsAPIErrorDescriptor { - // (undocumented) - ID?: number; - // (undocumented) - message: string; + id: number; + message?: string; } -// @public (undocumented) +// @public export class DevOpsAPIResponseError extends DevOpsAPIError { + // @internal constructor(error: AxiosError); - // (undocumented) readonly errors: DevOpsAPIErrorDescriptor[]; - // (undocumented) - readonly rootError: AxiosError; - // (undocumented) readonly status?: number; } -// @public (undocumented) -export class DevOpsAPITimeout extends DataAPIError { +// @public +export class DevOpsAPITimeout extends DevOpsAPIError { + // @internal constructor(url: string, timeout: number); - // (undocumented) readonly timeout: number; - // (undocumented) readonly url: string; } -// @public (undocumented) +// @public export class DevOpsUnexpectedStateError extends DevOpsAPIError { + // @internal constructor(message: string, raw?: AxiosResponse); - // (undocumented) - readonly rawResponse?: AxiosResponse; - // (undocumented) + readonly dbInfo?: FullDatabaseInfo; readonly status?: number; } @@ -597,13 +563,13 @@ export type FilterOps = { $in?: Elem[]; $nin?: Elem[]; $exists?: boolean; -} & (IsNum extends false ? {} : NumFilterOps) & (IsDate extends false ? {} : DateFilterOps) & (any[] extends Elem ? ArrayFilterOps : {}); +} & (IsNum extends false ? {} : NumFilterOps) & (IsDate extends false ? {} : (DateFilterOps | Date)) & (any[] extends Elem ? ArrayFilterOps : {}); // @public export class FindCursor { [Symbol.asyncIterator](): AsyncGenerator; // @internal - constructor(namespace: string, httpClient: DataAPIHttpClient, filter: Filter, options?: FindOptions); + constructor(namespace: string, httpClient: DataAPIHttpClient, filter: Filter, options?: FindOptions); // (undocumented) bufferedCount(): number; clone(): FindCursor; @@ -663,8 +629,8 @@ export interface FindOneAndUpdateOptions extends WithTimeout { } // @public -export interface FindOneOptions extends WithTimeout { - includeSimilarity?: GetSim; +export interface FindOneOptions extends WithTimeout { + includeSimilarity?: boolean; projection?: Projection; sort?: Sort; vector?: number[]; @@ -673,8 +639,8 @@ export interface FindOneOptions extends WithTimeout { } // @public -export interface FindOptions { - includeSimilarity?: GetSim; +export interface FindOptions { + includeSimilarity?: boolean; limit?: number; projection?: Projection; skip?: number; @@ -688,7 +654,9 @@ export interface FindOptions { export type Flatten = Type extends (infer Item)[] ? Item : Type; // @public -export type FoundDoc = WithSim, GetSim>; +export type FoundDoc = WithId & { + $similarity?: number; +}>; // @public export interface FullCollectionInfo { @@ -738,10 +706,8 @@ export type IndexingOptions = { // @public export class InsertManyError extends CumulativeDataAPIError { - // (undocumented) name: string; - // (undocumented) - readonly partialResult: InsertManyResult; + readonly partialResult: InsertManyResult; } // @public @@ -757,7 +723,7 @@ export interface InsertManyOrderedOptions extends WithTimeout { } // @public -export interface InsertManyResult { +export interface InsertManyResult { insertedCount: number; insertedIds: IdOf[]; } @@ -772,9 +738,8 @@ export interface InsertManyUnorderedOptions extends WithTimeout { vectors?: (number[] | null | undefined)[]; } -// @public (undocumented) +// @public export interface InsertOneModel { - // (undocumented) document: TSchema; } @@ -849,20 +814,16 @@ export interface NoUpsertUpdateOptions { upsertedId?: never; } -// @public (undocumented) +// @public export type NumberUpdate = { [K in keyof Schema as IsNum extends true ? K : never]?: number | bigint; }; // @public export interface NumFilterOps { - // (undocumented) $gt?: number | bigint; - // (undocumented) $gte?: number | bigint; - // (undocumented) $lt?: number | bigint; - // (undocumented) $lte?: number | bigint; } @@ -871,9 +832,7 @@ export class ObjectId { constructor(id?: string, validate?: boolean); equals(other: unknown): boolean; getTimestamp(): Date; - // (undocumented) inspect(): string; - // (undocumented) toJSON(): { $objectId: string; }; @@ -886,7 +845,7 @@ export interface PollBlockingOptions extends WithTimeout { pollInterval?: number; } -// @public (undocumented) +// @public export type Pop = { [K in keyof ArrayUpdate]?: number; }; @@ -900,7 +859,7 @@ export interface ProjectionSlice { $slice: number | [number, number]; } -// @public (undocumented) +// @public export type Push = { [K in keyof ArrayUpdate]?: (ArrayUpdate[K] | { $each: ArrayUpdate[K][]; @@ -918,18 +877,10 @@ export interface RawDataAPIResponse { status?: Record; } -// @public (undocumented) -export type Rename = { - [K in keyof Schema]?: string; -}; - -// @public (undocumented) +// @public export interface ReplaceOneModel { - // (undocumented) filter: Filter; - // (undocumented) - replacement: TSchema; - // (undocumented) + replacement: NoId; upsert?: boolean; } @@ -957,15 +908,18 @@ export type SomeDoc = Record; export type SomeId = string | number | bigint | boolean | Date | UUID | ObjectId; // @public -export type Sort = Record | { +export type Sort = Record | { $vector: number[]; } | { $vectorize: string; }; // @public (undocumented) -export type StrictDateUpdate = ContainsDate extends true ? { - [K in keyof Schema as ContainsDate extends true ? K : never]?: Date | { +export type SortDirection = 1 | -1; + +// @public +export type StrictDateUpdate> = ContainsDate extends true ? { + [K in keyof InNotation as ContainsDate extends true ? K : never]?: Date | { $date: number; }; } : TypeErr<'Can not perform a date operation on a schema with no dates'>; @@ -982,16 +936,16 @@ export type StrictFilter = { // Warning: (ae-forgotten-export) The symbol "ContainsNum" needs to be exported by the entry point index.d.ts // -// @public (undocumented) -export type StrictNumberUpdate = ContainsNum extends true ? { - [K in keyof Schema as IsNum extends true ? K : never]?: number | bigint; +// @public +export type StrictNumberUpdate> = ContainsNum extends true ? { + [K in keyof InNotation as IsNum extends true ? K : never]?: number | bigint; } : TypeErr<'Can not perform a number operation on a schema with no numbers'>; // Warning: (ae-forgotten-export) The symbol "ContainsArr" needs to be exported by the entry point index.d.ts // -// @public (undocumented) -export type StrictPop = ContainsArr extends true ? { - [K in keyof ArrayUpdate]?: number; +// @public +export type StrictPop> = ContainsArr extends true ? { + [K in keyof ArrayUpdate]?: number; } : TypeErr<'Can not pop on a schema with no arrays'>; // @public @@ -999,14 +953,19 @@ export type StrictProjection = { [K in keyof ToDotNotation>]?: any[] extends (ToDotNotation>)[K] ? 1 | 0 | true | false | ProjectionSlice : 1 | 0 | true | false; }; -// @public (undocumented) -export type StrictPush = ContainsArr extends true ? { - [K in keyof ArrayUpdate]?: (ArrayUpdate[K] | { - $each: ArrayUpdate[K][]; +// @public +export type StrictPush> = ContainsArr extends true ? { + [K in keyof ArrayUpdate]?: (ArrayUpdate[K] | { + $each: ArrayUpdate[K][]; $position?: number; }); } : TypeErr<'Can not perform array operation on a schema with no arrays'>; +// @public +export type StrictRename = { + [K in keyof ToDotNotation]?: string; +}; + // @public export type StrictSort = { [K in keyof ToDotNotation>]?: 1 | -1; @@ -1017,19 +976,24 @@ export type StrictSort = { }; // @public -export interface StrictUpdateFilter> { - $addToSet?: StrictPush; - $currentDate?: CurrentDate; - $inc?: StrictNumberUpdate; - $max?: StrictNumberUpdate | StrictDateUpdate; - $min?: StrictNumberUpdate | StrictDateUpdate; - $mul?: StrictNumberUpdate; - $pop?: StrictPop; - $push?: StrictPush; - $rename?: Rename; - $set?: Partial; - $setOnInsert?: Partial; - $unset?: Unset; +export type StrictUnset = { + [K in keyof ToDotNotation]?: '' | true | 1; +}; + +// @public +export interface StrictUpdateFilter { + $addToSet?: StrictPush; + $currentDate?: CurrentDate>; + $inc?: StrictNumberUpdate; + $max?: StrictNumberUpdate | StrictDateUpdate; + $min?: StrictNumberUpdate | StrictDateUpdate; + $mul?: StrictNumberUpdate; + $pop?: StrictPop; + $push?: StrictPush; + $rename?: StrictRename; + $set?: Partial>; + $setOnInsert?: Partial>; + $unset?: StrictUnset; } // Warning: (ae-forgotten-export) The symbol "Merge" needs to be exported by the entry point index.d.ts @@ -1045,8 +1009,6 @@ export class TooManyDocsToCountError extends DataAPIError { readonly hitServerLimit: boolean; // (undocumented) readonly limit: number; - // (undocumented) - name: string; } // @public @@ -1054,11 +1016,6 @@ export type TypeErr = unknown & { [__error]: S; }; -// @public (undocumented) -export type Unset = { - [K in keyof Schema]?: '' | true | 1; -}; - // @public export interface UpdateFilter { $addToSet?: Push & SomeDoc; @@ -1081,19 +1038,14 @@ export interface UpdateFilter { // @public export class UpdateManyError extends CumulativeDataAPIError { - // (undocumented) name: string; - // (undocumented) readonly partialResult: UpdateManyResult; } -// @public (undocumented) +// @public export interface UpdateManyModel { - // (undocumented) filter: Filter; - // (undocumented) update: UpdateFilter; - // (undocumented) upsert?: boolean; } @@ -1105,13 +1057,10 @@ export interface UpdateManyOptions extends WithTimeout { // @public export type UpdateManyResult = InternalUpdateResult; -// @public (undocumented) +// @public export interface UpdateOneModel { - // (undocumented) filter: Filter; - // (undocumented) update: UpdateFilter; - // (undocumented) upsert?: boolean; } @@ -1138,9 +1087,7 @@ export class UUID { constructor(uuid: string, validate?: boolean); equals(other: unknown): boolean; getTimestamp(): Date | undefined; - // (undocumented) inspect(): string; - // (undocumented) toJSON(): { $uuid: string; }; @@ -1181,13 +1128,6 @@ export interface WithNamespace { namespace?: string; } -// @public -export type WithSim = GetSim extends true ? Omit & { - $similarity: number; -} : Omit & { - $similarity?: never; -}; - // @public export interface WithTimeout { maxTimeMS?: number; diff --git a/package-lock.json b/package-lock.json index c8491805..3306b40f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,18 +1,16 @@ { "name": "@datastax/astra-db-ts", - "version": "1.0.0-alpha.1", + "version": "0.1.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@datastax/astra-db-ts", - "version": "1.0.0-alpha.1", - "hasInstallScript": true, + "version": "0.1.4", "license": "Apache-2.0", "dependencies": { - "@microsoft/api-extractor": "^7.43.0", "axios": "^1.6.7", - "bson": "^6.3.0", + "bson-objectid": "^2.0.4", "object-hash": "^3.0.0", "typed-emitter": "^2.1.0", "uuidv7": "^0.6.3" @@ -24,6 +22,7 @@ "@babel/plugin-proposal-object-rest-spread": "^7.18.0", "@babel/preset-env": "^7.18.2", "@babel/preset-typescript": "^7.17.12", + "@microsoft/api-extractor": "^7.43.0", "@types/expect": "^24.3.0", "@types/mocha": "^9.1.1", "@types/node": "^17.0.36", @@ -2223,6 +2222,7 @@ "version": "7.43.0", "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.43.0.tgz", "integrity": "sha512-GFhTcJpB+MI6FhvXEI9b2K0snulNLWHqC/BbcJtyNYcKUiw7l3Lgis5ApsYncJ0leALX7/of4XfmXk+maT111w==", + "dev": true, "dependencies": { "@microsoft/api-extractor-model": "7.28.13", "@microsoft/tsdoc": "0.14.2", @@ -2246,6 +2246,7 @@ "version": "7.28.13", "resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.28.13.tgz", "integrity": "sha512-39v/JyldX4MS9uzHcdfmjjfS6cYGAoXV+io8B5a338pkHiSt+gy2eXQ0Q7cGFJ7quSa1VqqlMdlPrB6sLR/cAw==", + "dev": true, "dependencies": { "@microsoft/tsdoc": "0.14.2", "@microsoft/tsdoc-config": "~0.16.1", @@ -2256,6 +2257,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, "dependencies": { "yallist": "^4.0.0" }, @@ -2267,6 +2269,7 @@ "version": "3.0.8", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz", "integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==", + "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -2278,6 +2281,7 @@ "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, "dependencies": { "lru-cache": "^6.0.0" }, @@ -2291,17 +2295,20 @@ "node_modules/@microsoft/api-extractor/node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true }, "node_modules/@microsoft/tsdoc": { "version": "0.14.2", "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.14.2.tgz", - "integrity": "sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==" + "integrity": "sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==", + "dev": true }, "node_modules/@microsoft/tsdoc-config": { "version": "0.16.2", "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.16.2.tgz", "integrity": "sha512-OGiIzzoBLgWWR0UdRJX98oYO+XKGf7tiK4Zk6tQ/E4IJqGCe7dvkTvgDZV5cFJUzLGDOjeAXrnZoA6QkVySuxw==", + "dev": true, "dependencies": { "@microsoft/tsdoc": "0.14.2", "ajv": "~6.12.6", @@ -2313,6 +2320,7 @@ "version": "1.19.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", + "dev": true, "dependencies": { "is-core-module": "^2.1.0", "path-parse": "^1.0.6" @@ -2367,6 +2375,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-4.0.2.tgz", "integrity": "sha512-hyES82QVpkfQMeBMteQUnrhASL/KHPhd7iJ8euduwNJG4mu2GSOKybf0rOEjOm1Wz7CwJEUm9y0yD7jg2C1bfg==", + "dev": true, "dependencies": { "fs-extra": "~7.0.1", "import-lazy": "~4.0.0", @@ -2388,6 +2397,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, "dependencies": { "yallist": "^4.0.0" }, @@ -2399,6 +2409,7 @@ "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, "dependencies": { "lru-cache": "^6.0.0" }, @@ -2412,12 +2423,14 @@ "node_modules/@rushstack/node-core-library/node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true }, "node_modules/@rushstack/rig-package": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.5.2.tgz", "integrity": "sha512-mUDecIJeH3yYGZs2a48k+pbhM6JYwWlgjs2Ca5f2n1G2/kgdgP9D/07oglEGf6mRyXEnazhEENeYTSNDRCwdqA==", + "dev": true, "dependencies": { "resolve": "~1.22.1", "strip-json-comments": "~3.1.1" @@ -2427,6 +2440,7 @@ "version": "0.10.0", "resolved": "https://registry.npmjs.org/@rushstack/terminal/-/terminal-0.10.0.tgz", "integrity": "sha512-UbELbXnUdc7EKwfH2sb8ChqNgapUOdqcCIdQP4NGxBpTZV2sQyeekuK3zmfQSa/MN+/7b4kBogl2wq0vpkpYGw==", + "dev": true, "dependencies": { "@rushstack/node-core-library": "4.0.2", "supports-color": "~8.1.1" @@ -2444,6 +2458,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, "engines": { "node": ">=8" } @@ -2452,6 +2467,7 @@ "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -2466,6 +2482,7 @@ "version": "4.19.1", "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.19.1.tgz", "integrity": "sha512-J7H768dgcpG60d7skZ5uSSwyCZs/S2HrWP1Ds8d1qYAyaaeJmpmmLr9BVw97RjFzmQPOYnoXcKA4GkqDCkduQg==", + "dev": true, "dependencies": { "@rushstack/terminal": "0.10.0", "@types/argparse": "1.0.38", @@ -2477,6 +2494,7 @@ "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, "dependencies": { "sprintf-js": "~1.0.2" } @@ -2490,7 +2508,8 @@ "node_modules/@types/argparse": { "version": "1.0.38", "resolved": "https://registry.npmjs.org/@types/argparse/-/argparse-1.0.38.tgz", - "integrity": "sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==" + "integrity": "sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==", + "dev": true }, "node_modules/@types/expect": { "version": "24.3.0", @@ -2549,7 +2568,7 @@ "version": "17.0.45", "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz", "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==", - "devOptional": true + "dev": true }, "node_modules/@types/object-hash": { "version": "3.0.6", @@ -2956,6 +2975,7 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -3110,7 +3130,8 @@ "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true }, "node_modules/binary-extensions": { "version": "2.2.0", @@ -3125,6 +3146,7 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -3181,13 +3203,10 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/bson": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/bson/-/bson-6.3.0.tgz", - "integrity": "sha512-balJfqwwTBddxfnidJZagCBPP/f48zj9Sdp3OJswREOgsJzHiQSaOIAtApSgDQFYgHqAvFkp53AFSqjMDZoTFw==", - "engines": { - "node": ">=16.20.1" - } + "node_modules/bson-objectid": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/bson-objectid/-/bson-objectid-2.0.4.tgz", + "integrity": "sha512-vgnKAUzcDoa+AeyYwXCoHyF2q6u/8H46dxu5JN+4/TZeq/Dlinn0K6GvxsCLb3LHUJl0m/TLiEK31kUwtgocMQ==" }, "node_modules/buffer-from": { "version": "1.1.2", @@ -3384,7 +3403,8 @@ "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true }, "node_modules/convert-source-map": { "version": "2.0.0", @@ -3860,7 +3880,8 @@ "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true }, "node_modules/fast-glob": { "version": "3.3.2", @@ -3881,7 +3902,8 @@ "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true }, "node_modules/fast-levenshtein": { "version": "2.0.6", @@ -4069,6 +4091,7 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, "dependencies": { "graceful-fs": "^4.1.2", "jsonfile": "^4.0.0", @@ -4108,6 +4131,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -4212,7 +4236,8 @@ "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true }, "node_modules/graphemer": { "version": "1.4.0", @@ -4249,6 +4274,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "dev": true, "dependencies": { "function-bind": "^1.1.2" }, @@ -4301,6 +4327,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", + "dev": true, "engines": { "node": ">=8" } @@ -4355,6 +4382,7 @@ "version": "2.13.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dev": true, "dependencies": { "hasown": "^2.0.0" }, @@ -4997,7 +5025,8 @@ "node_modules/jju": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", - "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==" + "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==", + "dev": true }, "node_modules/js-tokens": { "version": "4.0.0", @@ -5060,7 +5089,8 @@ "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", @@ -5084,6 +5114,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, "optionalDependencies": { "graceful-fs": "^4.1.6" } @@ -5128,7 +5159,8 @@ "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true }, "node_modules/lodash.debounce": { "version": "4.0.8", @@ -5145,12 +5177,14 @@ "node_modules/lodash.get": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==" + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", + "dev": true }, "node_modules/lodash.isequal": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", - "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", + "dev": true }, "node_modules/lodash.merge": { "version": "4.6.2", @@ -5923,7 +5957,8 @@ "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true }, "node_modules/path-type": { "version": "4.0.0", @@ -6093,6 +6128,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, "engines": { "node": ">=6" } @@ -6256,6 +6292,7 @@ "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", @@ -6420,6 +6457,7 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -6469,7 +6507,8 @@ "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true }, "node_modules/stack-utils": { "version": "2.0.6", @@ -6496,6 +6535,7 @@ "version": "0.3.2", "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "dev": true, "engines": { "node": ">=0.6.19" } @@ -6539,6 +6579,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, "engines": { "node": ">=8" }, @@ -6562,6 +6603,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, "engines": { "node": ">= 0.4" }, @@ -6805,6 +6847,7 @@ "version": "5.4.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz", "integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==", + "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -6857,6 +6900,7 @@ "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, "engines": { "node": ">= 4.0.0" } @@ -6895,6 +6939,7 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, "dependencies": { "punycode": "^2.1.0" } @@ -6920,6 +6965,7 @@ "version": "13.11.0", "resolved": "https://registry.npmjs.org/validator/-/validator-13.11.0.tgz", "integrity": "sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==", + "dev": true, "engines": { "node": ">= 0.10" } @@ -7127,6 +7173,7 @@ "version": "5.0.5", "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.5.tgz", "integrity": "sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==", + "dev": true, "dependencies": { "lodash.get": "^4.4.2", "lodash.isequal": "^4.5.0", @@ -7146,6 +7193,7 @@ "version": "9.5.0", "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "dev": true, "optional": true, "engines": { "node": "^12.20.0 || >=14" diff --git a/package.json b/package.json index 8baeca9b..ee399220 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@datastax/astra-db-ts", - "version": "1.0.0-alpha.1", + "version": "0.1.4", "description": "Astra DB TS Client", "contributors": [ "Kavin Gupta (https://github.com/toptobes)", @@ -33,19 +33,11 @@ "tests/setup.ts" ] }, - "madge": { - "detectiveOptions": { - "ts": { - "skipTypeImports": true - } - } - }, "directories": { "lib": "src", "test": "tests" }, "files": [ - "src", "dist" ], "publishConfig": { @@ -58,14 +50,12 @@ "scripts": { "lint": "eslint .", "test": "ts-mocha --paths -p tsconfig.json tests/unit/**/*.test.ts tests/integration/**/*.test.ts tests/integration/**/**/*.test.ts", - "test:integration": "ts-mocha --paths -p tsconfig.json tests/integration/**/**/*.test.ts", - "test:unit": "ts-mocha --paths -p tsconfig.json tests/unit/**/*.test.ts", - "test:coverage": "nyc npm run test -- -b", + "test:all": "env ASTRA_RUN_LONG_TESTS=1 ASTRA_RUN_ADMIN_TESTS=1 ts-mocha --paths -p tsconfig.json tests/unit/**/*.test.ts tests/integration/**/*.test.ts tests/integration/**/**/*.test.ts", + "test:coverage": "nyc npm run test:all -- -b", "test:types": "tsc --noEmit --skipLibCheck", - "preinstall": "npm run update-version-file", + "api-extractor": "api-extractor run -c ./api-extractor.jsonc --local", "build": "sh ./scripts/build.sh", - "update-version-file": "node -p \"'export const LIB_NAME = ' + JSON.stringify('astra-db-ts') + ';'\" > src/version.ts && node -p \"'export const LIB_VERSION = ' + JSON.stringify(require('./package.json').version) + ';'\" >> src/version.ts", - "api-extractor": "api-extractor run -c ./api-extractor.jsonc --local" + "version": "npm run build && git add src/version.ts etc/astra-db-ts.api.md" }, "bugs": { "url": "https://github.com/datastax/astra-ts-client/issues" @@ -77,6 +67,7 @@ "@babel/plugin-proposal-object-rest-spread": "^7.18.0", "@babel/preset-env": "^7.18.2", "@babel/preset-typescript": "^7.17.12", + "@microsoft/api-extractor": "^7.43.0", "@types/expect": "^24.3.0", "@types/mocha": "^9.1.1", "@types/node": "^17.0.36", @@ -93,9 +84,8 @@ "typescript": "^5.3.3" }, "dependencies": { - "@microsoft/api-extractor": "^7.43.0", "axios": "^1.6.7", - "bson": "^6.3.0", + "bson-objectid": "^2.0.4", "object-hash": "^3.0.0", "typed-emitter": "^2.1.0", "uuidv7": "^0.6.3" diff --git a/scripts/build.sh b/scripts/build.sh index 429b94dc..fcf93e8a 100644 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -1,7 +1,7 @@ #!/usr/bin/sh rm -rf ./dist -node -p "'export const LIB_NAME = ' + JSON.stringify('astra-db-ts') + ';'" > src/version.ts +echo "export const LIB_NAME = 'astra-db-ts';" > src/version.ts node -p "'export const LIB_VERSION = ' + JSON.stringify(require('./package.json').version) + ';'" >> src/version.ts npx tsc --project tsconfig.build.json npx tsc-alias -p tsconfig.build.json diff --git a/src/api/data-api-http-client.ts b/src/api/data-api-http-client.ts index 770a9386..d71be741 100644 --- a/src/api/data-api-http-client.ts +++ b/src/api/data-api-http-client.ts @@ -17,7 +17,8 @@ import { DEFAULT_NAMESPACE, DEFAULT_TIMEOUT, hrTimeMs, HttpClient, HttpMethods, import { DataAPIResponseError, DataAPITimeout, ObjectId, UUID } from '@/src/data-api'; import { MkTimeoutError, TimeoutManager, TimeoutOptions } from '@/src/api/timeout-managers'; import { CommandFailedEvent, CommandStartedEvent, CommandSucceededEvent } from '@/src/data-api/events'; -import { mkRespErrorFromResponse } from '@/src/data-api/errors'; +import { CollectionNotFoundError, mkRespErrorFromResponse } from '@/src/data-api/errors'; +import * as util from 'util'; /** * @internal @@ -87,6 +88,11 @@ export class DataAPIHttpClient extends HttpClient { throw mkRespErrorFromResponse(DataAPIResponseError, info.command, fauxResponse); } + if (response.data?.errors?.length > 0 && response.data?.errors[0]?.errorCode === 'COLLECTION_NOT_EXIST') { + const name = response.data?.errors[0]?.message.split(': ')[1]; + throw new CollectionNotFoundError(info.namespace, name); + } + if (response.status === 200) { if (response.data?.errors && response.data?.errors.length > 0) { throw mkRespErrorFromResponse(DataAPIResponseError, info.command, response.data); diff --git a/src/client/data-api-client.ts b/src/client/data-api-client.ts index 62fde340..8bca77ce 100644 --- a/src/client/data-api-client.ts +++ b/src/client/data-api-client.ts @@ -28,6 +28,18 @@ import { AdminCommandEvents } from '@/src/devops'; import { validateOption } from '@/src/data-api/utils'; /** + * The events emitted by the {@link DataAPIClient}. These events are emitted at various stages of the + * command's lifecycle. Intended for use for monitoring and logging purposes. + * + * Events include: + * - `commandStarted` - Emitted when a command is started, before the initial HTTP request is made. + * - `commandSucceeded` - Emitted when a command has succeeded. + * - `commandFailed` - Emitted when a command has errored. + * - `adminCommandStarted` - Emitted when an admin command is started, before the initial HTTP request is made. + * - `adminCommandPolling` - Emitted when a command is polling in a long-running operation (i.e. create database). + * - `adminCommandSucceeded` - Emitted when an admin command has succeeded, after any necessary polling. + * - `adminCommandFailed` - Emitted when an admin command has errored. + * * @public */ export type DataAPIClientEvents = @@ -35,6 +47,9 @@ export type DataAPIClientEvents = & AdminCommandEvents /** + * The base class for the {@link DataAPIClient} event emitter to make it properly typed. Should probably never need + * to be used directly. + * * @public */ export const DataAPIClientEventEmitterBase = EventEmitter as (new () => TypedEmitter); diff --git a/src/data-api/collection.ts b/src/data-api/collection.ts index d4aacea3..7aaf0efa 100644 --- a/src/data-api/collection.ts +++ b/src/data-api/collection.ts @@ -14,9 +14,10 @@ import { takeWhile } from './utils'; import { FindCursor } from '@/src/data-api/cursor'; -import { Db, SomeDoc } from '@/src/data-api'; +import { Db, SomeDoc, SomeId } from '@/src/data-api'; import { BulkWriteError, + CollectionNotFoundError, DataAPIResponseError, DeleteManyError, InsertManyError, @@ -1436,7 +1437,7 @@ export class Collection { const collection = results.find((c) => c.name === this.collectionName); if (!collection) { - throw new Error(`Collection '${this.collectionName}' not found`); + throw new CollectionNotFoundError(this.namespace, this.collectionName); } return collection.options ?? {}; @@ -1514,7 +1515,7 @@ const insertManyOrdered = async (httpClient: DataAPIHttpClient, document const desc = e.detailedErrorDescriptors[0]; insertedIds.push(...desc.rawResponse.status?.insertedIds ?? []); - throw mkRespErrorFromResponse(InsertManyError, desc.command, desc.rawResponse, { insertedIds: insertedIds, insertedCount: insertedIds.length }) + throw mkRespErrorFromResponse(InsertManyError, desc.command, desc.rawResponse, { insertedIds: insertedIds as SomeId[], insertedCount: insertedIds.length }) } } @@ -1560,7 +1561,7 @@ const insertManyUnordered = async (httpClient: DataAPIHttpClient, docume await Promise.all(workers); if (failCommands.length > 0) { - throw mkRespErrorFromResponses(InsertManyError, failCommands, failRaw, { insertedIds: insertedIds, insertedCount: insertedIds.length }); + throw mkRespErrorFromResponses(InsertManyError, failCommands, failRaw, { insertedIds: insertedIds as SomeId[], insertedCount: insertedIds.length }); } return insertedIds; diff --git a/src/data-api/cursor.ts b/src/data-api/cursor.ts index e9bfd05c..78733ee7 100644 --- a/src/data-api/cursor.ts +++ b/src/data-api/cursor.ts @@ -18,7 +18,7 @@ import { Projection, Sort, } from '@/src/data-api/types'; -import { CursorAlreadyInitializedError, SomeDoc } from '@/src/data-api'; +import { CursorIsStartedError, SomeDoc } from '@/src/data-api'; import { DataAPIHttpClient } from '@/src/api'; import { InternalFindOptions, InternalGetMoreCommand } from '@/src/data-api/types/find/find'; @@ -433,7 +433,7 @@ export class FindCursor { private _assertUninitialized(): void { if (this._state !== CursorStatus.Uninitialized) { - throw new CursorAlreadyInitializedError('Cursor is already initialized/in use; cannot perform options modification. Rewind or clone the cursor.'); + throw new CursorIsStartedError('Cursor is already initialized/in use; cannot perform options modification. Rewind or clone the cursor.'); } } diff --git a/src/data-api/errors.ts b/src/data-api/errors.ts index 02663b8e..5bb1a3f6 100644 --- a/src/data-api/errors.ts +++ b/src/data-api/errors.ts @@ -130,11 +130,22 @@ export interface DataAPIDetailedErrorDescriptor { export abstract class DataAPIError extends Error {} /** + * An error thrown when a Data API operation timed out. + * + * Depending on the method, this may be a request timeout occurring during a specific HTTP request, or can happen over + * the course of a method involving several requests in a row, such as a paginated `insertMany`. + * * @public */ export class DataAPITimeout extends DataAPIError { - constructor(readonly timeout: number) { + /** + * The timeout that was set for the operation, in milliseconds. + */ + public readonly timeout: number; + + constructor(timeout: number) { super(`Command timed out after ${timeout}ms`); + this.timeout = timeout; this.name = 'DataAPITimeout'; } } @@ -163,13 +174,12 @@ export class DataAPITimeout extends DataAPIError { * @public */ export class TooManyDocsToCountError extends DataAPIError { - name = 'TooManyDocsToCountError' - constructor(readonly limit: number, readonly hitServerLimit: boolean) { const message = (hitServerLimit) ? `Too many documents to count (server limit of ${limit} reached)` : `Too many documents to count (provided limit is ${limit})`; super(message); + this.name = 'TooManyDocsToCountError'; } } @@ -195,7 +205,7 @@ export class TooManyDocsToCountError extends DataAPIError { * * @public */ -export class CursorAlreadyInitializedError extends DataAPIError { +export class CursorIsStartedError extends DataAPIError { constructor(message: string) { super(message); this.name = 'CursorAlreadyInitializedError'; @@ -203,11 +213,56 @@ export class CursorAlreadyInitializedError extends DataAPIError { } /** + * An exception thrown when certain operations are attempted on a collection that does not exist. + * + * @field namespace - The namespace that the collection was not found in + * @field collectionName - The name of the collection that was not found + * + * @public + */ +export class CollectionNotFoundError extends DataAPIError { + /** + * The namespace where the collection already exists + */ + public readonly namespace: string; + + /** + * The name of the collection that already exists + */ + public readonly collectionName: string; + + constructor(namespace: string, collectionName: string) { + super(`Collection '${namespace}.${collectionName}' not found`); + this.namespace = namespace; + this.collectionName = collectionName; + this.name = 'CollectionNotFoundError'; + } +} + +/** + * An exception thrown when an operation that expects a collection not to exist is attempted on a collection that + * already exists. + * + * @field namespace - The namespace where the collection already exists + * @field collectionName - The name of the collection that already exists + * * @public */ export class CollectionAlreadyExistsError extends DataAPIError { - constructor(readonly namespace: string, readonly collectionName: string) { + /** + * The namespace where the collection already exists + */ + public readonly namespace: string; + + /** + * The name of the collection that already exists + */ + public readonly collectionName: string; + + constructor(namespace: string, collectionName: string) { super(`Collection '${namespace}.${collectionName}' already exists`); + this.namespace = namespace; + this.collectionName = collectionName; this.name = 'CollectionAlreadyExistsError'; } } @@ -216,7 +271,7 @@ export class CollectionAlreadyExistsError extends DataAPIError { * An error representing the *complete* errors for an operation. This is a cohesive error that represents all the * errors that occurred during a single operation, and should not be thought of as *always* 1:1 with the number of * API requests—rather it's 1:1 with the number of *logical* operations performed by the user (i.e. the methods - * on the {@link Collection} class. + * on the {@link Collection} class). * * This is *not* used for "hard" (4XX, 5XX) errors, which are rarer and would be thrown directly by the underlying * code. @@ -306,8 +361,16 @@ export abstract class CumulativeDataAPIError extends DataAPIResponseError { * @public */ export class InsertManyError extends CumulativeDataAPIError { + /** + * The name of the error. This is always 'InsertManyError'. + */ name = 'InsertManyError'; - declare public readonly partialResult: InsertManyResult; + + /** + * The partial result of the `InsertMany` operation that was performed. This is *always* defined, and is the result + * of all successful insertions. + */ + declare public readonly partialResult: InsertManyResult; } /** @@ -324,7 +387,15 @@ export class InsertManyError extends CumulativeDataAPIError { * @public */ export class DeleteManyError extends CumulativeDataAPIError { + /** + * The name of the error. This is always 'DeleteManyError'. + */ name = 'DeleteManyError'; + + /** + * The partial result of the `DeleteMany` operation that was performed. This is *always* defined, and is the result + * of the operation up to the point of the first error. + */ declare public readonly partialResult: DeleteManyResult; } @@ -342,7 +413,15 @@ export class DeleteManyError extends CumulativeDataAPIError { * @public */ export class UpdateManyError extends CumulativeDataAPIError { + /** + * The name of the error. This is always 'UpdateManyError'. + */ name = 'UpdateManyError'; + + /** + * The partial result of the `UpdateMany` operation that was performed. This is *always* defined, and is the result + * of the operation up to the point of the first error. + */ declare public readonly partialResult: UpdateManyResult; } @@ -363,7 +442,15 @@ export class UpdateManyError extends CumulativeDataAPIError { * @public */ export class BulkWriteError extends CumulativeDataAPIError { + /** + * The name of the error. This is always 'BulkWriteError'. + */ name = 'BulkWriteError'; + + /** + * The partial result of the `BulkWrite` operation that was performed. This is *always* defined, and is the result + * of all successful operations. + */ declare public readonly partialResult: BulkWriteResult; } diff --git a/src/data-api/events.ts b/src/data-api/events.ts index e688e910..8f0c1d09 100644 --- a/src/data-api/events.ts +++ b/src/data-api/events.ts @@ -18,6 +18,9 @@ import { DataAPIRequestInfo, DEFAULT_NAMESPACE, hrTimeMs, RawDataAPIResponse } f * The events emitted by the {@link DataAPIClient}. These events are emitted at various stages of the * command's lifecycle. Intended for use for monitoring and logging purposes. * + * **Note that these emit *real* commands, not any abstracted commands like "bulkWrite", "insertMany", or "deleteAll", + * which have to be translated into appropriate Data API commands.** + * * @public */ export type DataAPICommandEvents = { @@ -38,6 +41,9 @@ export type DataAPICommandEvents = { /** * Common base class for all command events. * + * **Note that these emit *real* commands, not any abstracted commands like "bulkWrite", "insertMany", or "deleteAll", + * which have to be translated into appropriate Data API commands.** + * * @public */ export abstract class CommandEvent { @@ -79,6 +85,9 @@ export abstract class CommandEvent { /** * Emitted when a command is started, before the initial HTTP request is made. * + * **Note that these emit *real* commands, not any abstracted commands like "bulkWrite", "insertMany", or "deleteAll", + * which have to be translated into appropriate Data API commands.** + * * @public */ export class CommandStartedEvent extends CommandEvent { @@ -101,6 +110,9 @@ export class CommandStartedEvent extends CommandEvent { /** * Emitted when a command has succeeded. * + * **Note that these emit *real* commands, not any abstracted commands like "bulkWrite", "insertMany", or "deleteAll", + * which have to be translated into appropriate Data API commands.** + * * @public */ export class CommandSucceededEvent extends CommandEvent { @@ -128,6 +140,9 @@ export class CommandSucceededEvent extends CommandEvent { /** * Emitted when a command has errored. * + * **Note that these emit *real* commands, not any abstracted commands like "bulkWrite", "insertMany", or "deleteAll", + * which have to be translated into appropriate Data API commands.** + * * @public */ export class CommandFailedEvent extends CommandEvent { diff --git a/src/data-api/ids.ts b/src/data-api/ids.ts index 0ae7e5ea..7b04403d 100644 --- a/src/data-api/ids.ts +++ b/src/data-api/ids.ts @@ -13,7 +13,7 @@ // limitations under the License. import { uuidv4, uuidv7, UUID as UUIDv7 } from 'uuidv7'; -import { ObjectId as MongoObjectId } from 'bson'; +import MongoObjectId from 'bson-objectid'; const uuidRegex = new RegExp('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$'); @@ -144,11 +144,18 @@ export class UUID { return new UUID(uuidv7(), false); } + /** + * Inspects the UUID. + */ public inspect(): string { return `UUID("${this.toString()}")`; } - // noinspection JSUnusedGlobalSymbols + /** + * Converts the UUID to a JSON representation. + * + * Serializes to `{ $uuid: 'uuid' }`. + */ public toJSON() { return { $uuid: this.toString() }; } @@ -207,17 +214,17 @@ export class ObjectId { * @param validate - Whether to validate the ObjectId string. Defaults to `true`. */ constructor(id?: string, validate = true) { - if (validate && id) { - if (typeof id === 'string') { + if (validate) { + if (typeof id === 'string') { if (id.length !== 24 || !objectIdRegex.test(id)) { throw new Error('ObjectId must be a 24-character hex string'); } - } else { + } else if (id !== undefined && id !== null) { throw new Error('ObjectId must be a string'); } } - this._objectId = new MongoObjectId(id); + this._objectId = (id) ? MongoObjectId(id) : MongoObjectId(); } /** @@ -251,11 +258,18 @@ export class ObjectId { return this._objectId.toString(); } + /** + * Inspects the ObjectId. + */ public inspect(): string { return `ObjectId("${this.toString()}")`; } - // noinspection JSUnusedGlobalSymbols + /** + * Converts the ObjectId to a JSON representation. + * + * Serializes to `{ $objectId: 'objectId' }`. + */ public toJSON() { return { $objectId: this.toString() }; } diff --git a/src/data-api/index.ts b/src/data-api/index.ts index 7984397c..04bb17d8 100644 --- a/src/data-api/index.ts +++ b/src/data-api/index.ts @@ -29,7 +29,7 @@ export { TooManyDocsToCountError, InsertManyError, BulkWriteError, - CursorAlreadyInitializedError, + CursorIsStartedError, UpdateManyError, } from './errors'; export * from './events'; diff --git a/src/data-api/types/filter.ts b/src/data-api/types/filter.ts index a20860b5..5729803a 100644 --- a/src/data-api/types/filter.ts +++ b/src/data-api/types/filter.ts @@ -86,7 +86,6 @@ export type StrictFilter = { $not?: StrictFilter, } - /** * Represents an expression in a filter statement, such as an exact value, or a filter operator * @@ -110,7 +109,7 @@ export type FilterOps = { IsNum extends false ? {} : NumFilterOps ) & ( // eslint-disable-next-line @typescript-eslint/ban-types -- Intersection w/ {} is a "noop" here - IsDate extends false ? {} : DateFilterOps + IsDate extends false ? {} : (DateFilterOps | Date) ) & ( // eslint-disable-next-line @typescript-eslint/ban-types -- Intersection w/ {} is a "noop" here any[] extends Elem ? ArrayFilterOps : {} @@ -122,9 +121,21 @@ export type FilterOps = { * @public */ export interface NumFilterOps { + /** + * Less than (exclusive) some number + */ $lt?: number | bigint, + /** + * Less than or equal to some number + */ $lte?: number | bigint, + /** + * Greater than (exclusive) some number + */ $gt?: number | bigint, + /** + * Greater than or equal to some number + */ $gte?: number | bigint, } @@ -134,11 +145,30 @@ export interface NumFilterOps { * @public */ export interface DateFilterOps { - $lt?: Date | { $date: number }, - $lte?: Date | { $date: number }, - $gt?: Date | { $date: number }, - $gte?: Date | { $date: number }, - $date?: number, + /** + * Less than (exclusive) some date. + * + * `{ $date: number }` can be replaced with `new Date(number)`. + */ + $lt?: Date, + /** + * Less than or equal to some date. + * + * `{ $date: number }` can be replaced with `new Date(number)`. + */ + $lte?: Date, + /** + * Greater than (exclusive) some date. + * + * `{ $date: number }` can be replaced with `new Date(number)`. + */ + $gt?: Date, + /** + * Greater than or equal to some date. + * + * `{ $date: number }` can be replaced with `new Date(number)`. + */ + $gte?: Date, } /** @@ -147,6 +177,12 @@ export interface DateFilterOps { * @public */ export interface ArrayFilterOps { + /** + * Checks if the array is of a certain size. + */ $size?: number, + /** + * Checks if the array contains all the specified elements. + */ $all?: Elem, } diff --git a/src/data-api/types/insert/insert-many.ts b/src/data-api/types/insert/insert-many.ts index ca4b77a8..2bdabe04 100644 --- a/src/data-api/types/insert/insert-many.ts +++ b/src/data-api/types/insert/insert-many.ts @@ -14,6 +14,7 @@ import type { IdOf } from '@/src/data-api/types'; import { WithTimeout } from '@/src/common/types'; +import { SomeDoc } from '@/src/data-api'; /** @internal */ export interface InsertManyCommand { @@ -149,7 +150,7 @@ export interface InsertManyUnorderedOptions extends WithTimeout { * * @public */ -export interface InsertManyResult { +export interface InsertManyResult { /** * The IDs of the inserted documents (including the autogenerated IDs). * diff --git a/src/data-api/types/misc/bulk-write.ts b/src/data-api/types/misc/bulk-write.ts index d606b5f9..52d2da53 100644 --- a/src/data-api/types/misc/bulk-write.ts +++ b/src/data-api/types/misc/bulk-write.ts @@ -13,7 +13,7 @@ // limitations under the License. // noinspection DuplicatedCode -import type { IdOf, SomeDoc } from '@/src/data-api'; +import type { IdOf, NoId, SomeDoc } from '@/src/data-api'; import type { Filter, UpdateFilter } from '@/src/data-api/types'; import { WithTimeout } from '@/src/common/types'; @@ -70,29 +70,66 @@ export interface BulkWriteUnorderedOptions extends WithTimeout { } /** + * Represents the result of a bulk write operation. + * * @public */ export class BulkWriteResult { constructor( + /** + * The number of documents deleted. + */ readonly deletedCount: number = 0, + /** + * The number of documents inserted. + */ readonly insertedCount: number = 0, + /** + * The number of documents matched by an update operation. + */ readonly matchedCount: number = 0, + /** + * The number of documents modified. + */ readonly modifiedCount: number = 0, + /** + * The number of documents upserted. + */ readonly upsertedCount: number = 0, + /** + * Upserted document generated ids. Sparse array, indexed by the position of the upserted operation in the bulk + * write request. + */ readonly upsertedIds: Record> = {}, private readonly _raw: object[] = [], ) {} + /** + * Returns the raw, internal result. + * + * @returns the raw, internal result. + */ getRawResponse(): Record[] { return this._raw; } - getUpsertedIdAt(index: number): IdOf { + /** + * Returns the upserted id at the given index. + * + * @param index - The index of the upserted id to retrieve. + * + * @returns The upserted id at the given index, or `undefined` if there is no upserted id at that index. + */ + getUpsertedIdAt(index: number): IdOf | undefined { return this.upsertedIds[index]; } } /** + * Represents *some* bulk write operation. + * + * Be careful not to pass in multiple operations in the same object. + * * @public */ export type AnyBulkWriteOperation = { @@ -110,49 +147,131 @@ export type AnyBulkWriteOperation = { } /** + * Represents an insertOne operation that can be used in a bulk write operation. + * + * @field document - The document to insert. + * * @public */ export interface InsertOneModel { - document: TSchema; + /** + * The document to insert. + */ + document: TSchema, } /** + * Represents a replaceOne operation that can be used in a bulk write operation. + * + * @field filter - The filter to choose the document to replace. + * @field replacement - The replacement document, which contains no `_id` field. + * @field upsert - If true, perform an insert if no documents match the filter. + * * @public */ export interface ReplaceOneModel { - filter: Filter; - replacement: TSchema; - upsert?: boolean; + /** + * The filter to choose the document to replace. + */ + filter: Filter, + /** + * The replacement document, which contains no `_id` field. + */ + replacement: NoId, + /** + * If true, perform an insert if no documents match the filter. + * + * If false, do not insert if no documents match the filter. + * + * Defaults to false. + * + * @defaultValue false + */ + upsert?: boolean, } /** + * Represents an updateOne operation that can be used in a bulk write operation. + * + * @field filter - The filter to choose the document to update. + * @field update - The update to apply to the document. + * @field upsert - If true, perform an insert if no documents match the filter. + * * @public */ export interface UpdateOneModel { - filter: Filter; - update: UpdateFilter; - upsert?: boolean; + /** + * The filter to choose the document to update. + */ + filter: Filter, + /** + * The update to apply to the document. + */ + update: UpdateFilter, + /** + * If true, perform an insert if no documents match the filter. + * + * If false, do not insert if no documents match the filter. + * + * Defaults to false. + * + * @defaultValue false + */ + upsert?: boolean, } /** + * Represents an updateMany operation that can be used in a bulk write operation. + * + * @field filter - The filter to choose the documents to update. + * @field update - The update to apply to the documents. + * @field upsert - If true, perform an insert if no documents match the filter. + * * @public */ export interface UpdateManyModel { - filter: Filter; - update: UpdateFilter; - upsert?: boolean; + /** + * The filter to choose the documents to update. + */ + filter: Filter, + /** + * The update to apply to the documents. + */ + update: UpdateFilter, + /** + * If true, perform an insert if no documents match the filter. + * + * If false, do not insert if no documents match the filter. + * + * Defaults to false. + * + * @defaultValue false + */ + upsert?: boolean, } /** + * Represents a deleteOne operation that can be used in a bulk write operation. + * + * @field filter - The filter to choose the document to delete. + * * @public */ export interface DeleteOneModel { - filter: Filter; + /** + * The filter to choose the document to delete. + */ + filter: Filter, } /** + * Represents a deleteMany operation that can be used in a bulk write operation. + * * @public */ export interface DeleteManyModel { - filter: Filter; + /** + * The filter to choose the documents to delete. + */ + filter: Filter, } diff --git a/src/data-api/types/update-filter.ts b/src/data-api/types/update-filter.ts index bec40a67..fd1eef60 100644 --- a/src/data-api/types/update-filter.ts +++ b/src/data-api/types/update-filter.ts @@ -251,7 +251,7 @@ export interface UpdateFilter { * * @public */ -export interface StrictUpdateFilter> { +export interface StrictUpdateFilter { /** * Set the value of a field in the document. * @@ -264,7 +264,7 @@ export interface StrictUpdateFilter, + $set?: Partial>, /** * Set the value of a field in the document if an upsert is performed. * @@ -277,7 +277,7 @@ export interface StrictUpdateFilter, + $setOnInsert?: Partial>, /** * Remove the field from the document. * @@ -290,7 +290,7 @@ export interface StrictUpdateFilter, + $unset?: StrictUnset, /** * Increment the value of a field in the document if it's potentially a `number`. * @@ -303,7 +303,7 @@ export interface StrictUpdateFilter, + $inc?: StrictNumberUpdate, /** * Add an element to an array field in the document. * @@ -316,7 +316,7 @@ export interface StrictUpdateFilter, + $push?: StrictPush, /** * Remove an element from an array field in the document. * @@ -329,7 +329,7 @@ export interface StrictUpdateFilter, + $pop?: StrictPop, /** * Rename a field in the document. * @@ -342,7 +342,7 @@ export interface StrictUpdateFilter, + $rename?: StrictRename, /** * Set the value of a field to the current date. * @@ -355,7 +355,7 @@ export interface StrictUpdateFilter, + $currentDate?: CurrentDate>, /** * Only update the field if the specified value is less than the existing value. * @@ -368,7 +368,7 @@ export interface StrictUpdateFilter | StrictDateUpdate, + $min?: StrictNumberUpdate | StrictDateUpdate, /** * Only update the field if the specified value is greater than the existing value. * @@ -381,7 +381,7 @@ export interface StrictUpdateFilter | StrictDateUpdate, + $max?: StrictNumberUpdate | StrictDateUpdate, /** * Multiply the value of a field in the document. * @@ -394,7 +394,7 @@ export interface StrictUpdateFilter, + $mul?: StrictNumberUpdate, /** * Add an element to an array field in the document if it does not already exist. * @@ -407,17 +407,21 @@ export interface StrictUpdateFilter, + $addToSet?: StrictPush, } /** + * Very strongly types the unset operation (inc. dot notation schema). + * * @public */ -export type Unset = { - [K in keyof Schema]?: '' | true | 1 +export type StrictUnset = { + [K in keyof ToDotNotation]?: '' | true | 1 } /** + * Weaker version os StrictPop which allows for more flexibility in typing pop operations. + * * @public */ export type Pop ={ @@ -425,13 +429,17 @@ export type Pop ={ } /** + * Strongly types the pop operation (inc. dot notation schema). + * * @public */ -export type StrictPop = ContainsArr extends true ? { - [K in keyof ArrayUpdate]?: number +export type StrictPop> = ContainsArr extends true ? { + [K in keyof ArrayUpdate]?: number } : TypeErr<'Can not pop on a schema with no arrays'> /** + * Weaker version of StrictPush which allows for more flexibility in typing push operations. + * * @public */ export type Push = { @@ -442,23 +450,29 @@ export type Push = { } /** + * Strongly types the push operation (inc. dot notation schema). + * * @public */ -export type StrictPush = ContainsArr extends true ? { - [K in keyof ArrayUpdate]?: ( - | ArrayUpdate[K] - | { $each: ArrayUpdate[K][], $position?: number } +export type StrictPush> = ContainsArr extends true ? { + [K in keyof ArrayUpdate]?: ( + | ArrayUpdate[K] + | { $each: ArrayUpdate[K][], $position?: number } ) } : TypeErr<'Can not perform array operation on a schema with no arrays'> /** + * Strongly types the rename operation (inc. dot notation schema). + * * @public */ -export type Rename = { - [K in keyof Schema]?: string +export type StrictRename = { + [K in keyof ToDotNotation]?: string } /** + * Weaker version of StrictNumberUpdate which allows for more flexibility in typing number update operations. + * * @public */ export type NumberUpdate = { @@ -466,13 +480,17 @@ export type NumberUpdate = { } /** + * Strongly types number update operations (inc. dot notation schema). + * * @public */ -export type StrictNumberUpdate = ContainsNum extends true ? { - [K in keyof Schema as IsNum extends true ? K : never]?: number | bigint +export type StrictNumberUpdate> = ContainsNum extends true ? { + [K in keyof InNotation as IsNum extends true ? K : never]?: number | bigint } : TypeErr<'Can not perform a number operation on a schema with no numbers'>; /** + * Weaker version of StrictDateUpdate which allows for more flexibility in typing date update operations. + * * @public */ export type DateUpdate = { @@ -480,13 +498,17 @@ export type DateUpdate = { }; /** + * Strongly types date update operations (inc. dot notation schema). + * * @public */ -export type StrictDateUpdate = ContainsDate extends true ? { - [K in keyof Schema as ContainsDate extends true ? K : never]?: Date | { $date: number } +export type StrictDateUpdate> = ContainsDate extends true ? { + [K in keyof InNotation as ContainsDate extends true ? K : never]?: Date | { $date: number } } : TypeErr<'Can not perform a date operation on a schema with no dates'>; /** + * Types some array operations. Not inherently strict or weak. + * * @public */ export type ArrayUpdate = { @@ -494,6 +516,8 @@ export type ArrayUpdate = { }; /** + * Types the $currentDate operation. Not inherently strict or weak. + * * @public */ export type CurrentDate = { diff --git a/src/devops/errors.ts b/src/devops/errors.ts index ab4eb06e..cac7fb8c 100644 --- a/src/devops/errors.ts +++ b/src/devops/errors.ts @@ -13,59 +13,143 @@ // limitations under the License. import type { AxiosError, AxiosResponse } from 'axios'; -import { DataAPIError } from '@/src/data-api/errors'; +import { FullDatabaseInfo } from '@/src/devops/types'; /** + * A representation of what went wrong when interacting with the DevOps API. + * + * @field id - The API-specific error code. + * @field message - A user-friendly error message, if one exists (it most often does). + * * @public */ -export class DevOpsAPITimeout extends DataAPIError { - constructor(readonly url: string, readonly timeout: number) { - super(`Command timed out after ${timeout}ms`); - this.name = 'DevOpsAPITimeout'; - } +export interface DevOpsAPIErrorDescriptor { + /** + * The API-specific error code. + */ + id: number, + /** + * A user-friendly error message, if one exists (it most often does). + */ + message?: string, } /** + * An abstract class representing *some* exception that occurred related to the DevOps API. This is the base class for all + * DevOps API errors, and will never be thrown directly. + * + * Useful for `instanceof` checks. + * * @public */ -export interface DevOpsAPIErrorDescriptor { - ID?: number, - message: string, -} +export abstract class DevOpsAPIError extends Error {} /** + * An error thrown when an admin operation timed out. + * + * Depending on the method, this may be a request timeout occurring during a specific HTTP request, or can happen over + * the course of a method involving several requests in a row, such as a blocking `createDatabase`. + * + * @field url - The URL that the request was made to. + * @field timeout - The timeout that was set for the operation, in milliseconds. + * * @public */ -export abstract class DevOpsAPIError extends Error {} +export class DevOpsAPITimeout extends DevOpsAPIError { + /** + * The URL that the request was made to. + */ + public readonly url: string; + + /** + The timeout that was set for the operation, in milliseconds. + */ + public readonly timeout: number; + + /** + * Shouldn't be instantiated directly. + * + * @internal + */ + constructor(url: string, timeout: number) { + super(`Command timed out after ${timeout}ms`); + this.url = url; + this.timeout = timeout; + this.name = 'DevOpsAPITimeout'; + } +} /** + * An error representing a response from the DevOps API that was not successful (non-2XX status code). + * + * @field errors - The error descriptors returned by the API to describe what went wrong. + * @field rootError - The raw axios error that was thrown. + * @field status - The HTTP status code of the response, if available. + * * @public */ export class DevOpsAPIResponseError extends DevOpsAPIError { - readonly errors: DevOpsAPIErrorDescriptor[]; - readonly rootError: AxiosError; - readonly status?: number; + /** + * The error descriptors returned by the API to describe what went wrong. + */ + public readonly errors: DevOpsAPIErrorDescriptor[]; + + /** + * The HTTP status code of the response, if available. + */ + public readonly status?: number; + /** + * Shouldn't be instantiated directly. + * + * @internal + */ constructor(error: AxiosError) { super((error.response)?.data.errors[0]?.message ?? error.message); - this.errors = (error.response)?.data.errors ?? []; + this.errors = extractErrorDescriptors(error); this.status = (error.response)?.status; - this.rootError = error this.name = 'DevOpsAPIResponseError'; } } /** + * Error thrown when the DevOps API returns is in an unexpected state (i.e. `'PARKED'` when `'ACTIVE'` or `'PENDING'` + * was expected). + * + * @field dbInfo - The complete database info, which includes the status of the database. + * @field status - The HTTP status code of the response, if available. + * * @public */ export class DevOpsUnexpectedStateError extends DevOpsAPIError { - readonly status?: number; - readonly rawResponse?: AxiosResponse; + /** + * The HTTP status code of the response, if available. + */ + public readonly status?: number; + /** + * The complete database info, which includes the status of the database. + */ + public readonly dbInfo?: FullDatabaseInfo; + + /** + * Shouldn't be instantiated directly. + * + * @internal + */ constructor(message: string, raw?: AxiosResponse) { super(message); - this.rawResponse = raw; + this.dbInfo = raw?.data; this.status = raw?.status; this.name = 'DevOpsUnexpectedStateError'; } } + +function extractErrorDescriptors(error: AxiosError): DevOpsAPIErrorDescriptor[] { + const errors: any[] = (error.response)?.data.errors || []; + + return errors.map((e: any) => ({ + id: e.ID, + message: e.message, + })); +} diff --git a/src/devops/types/admin/admin-common.ts b/src/devops/types/admin/admin-common.ts index e774e279..b64cd4b2 100644 --- a/src/devops/types/admin/admin-common.ts +++ b/src/devops/types/admin/admin-common.ts @@ -16,21 +16,29 @@ import { WithTimeout } from '@/src/common/types'; /** + * Represents the available cloud providers that Astra offers. + * * @public */ export type DatabaseCloudProvider = 'AWS' | 'GCP' | 'AZURE'; /** + * Defines all possible compute powers (vertical scaling) for a database. + * * @public */ export type DatabaseTier = 'developer' | 'A5' | 'A10' | 'A20' | 'A40' | 'C10' | 'C20' | 'C40' | 'D10' | 'D20' | 'D40' | 'serverless'; /** + * Represents all possible statuses of a database. + * * @public */ export type DatabaseStatus = 'ACTIVE' | 'PENDING' | 'PREPARING' | 'PREPARED' | 'INITIALIZING' | 'PARKED' | 'PARKING' | 'UNPARKING' | 'TERMINATED' | 'TERMINATING' | 'RESIZING' | 'ERROR' | 'MAINTENANCE' | 'SUSPENDED' | 'UNKNOWN'; /** + * List of actions that can be performed on a database. + * * @public */ export type DatabaseAction = 'park' | 'unpark' | 'resize' | 'resetPassword' | 'addKeyspace' | 'addDatacenters' | 'terminateDatacenter' | 'getCreds' | 'terminate' | 'removeKeyspace' | 'removeMigrationProxy' | 'launchMigrationProxy'; diff --git a/src/devops/types/admin/create-database.ts b/src/devops/types/admin/create-database.ts index 6ba9fdf2..861e3fca 100644 --- a/src/devops/types/admin/create-database.ts +++ b/src/devops/types/admin/create-database.ts @@ -44,8 +44,13 @@ export interface DatabaseConfig { } /** + * Represents the options for creating a database (i.e. blocking options + database spawn options). + * * @public */ export type CreateDatabaseOptions = AdminBlockingOptions & { + /** + * Any options to override the default options set when creating the root {@link DataAPIClient}. + */ dbOptions?: DbSpawnOptions, } diff --git a/src/devops/types/admin/list-databases.ts b/src/devops/types/admin/list-databases.ts index 8f29c4c1..63e11fe6 100644 --- a/src/devops/types/admin/list-databases.ts +++ b/src/devops/types/admin/list-databases.ts @@ -16,11 +16,15 @@ import { DatabaseCloudProvider, DatabaseStatus, } from '@/src/devops/types'; import { WithTimeout } from '@/src/common/types'; /** + * Represents all possible statuses of a database that you can filter by. + * * @public */ export type DatabaseStatusFilter = DatabaseStatus | 'ALL' | 'NONTERMINATED'; /** + * Represents all possible cloud providers that you can filter by. + * * @public */ export type DatabaseCloudProviderFilter = DatabaseCloudProvider | 'ALL'; diff --git a/src/version.ts b/src/version.ts index c929baf1..14e57eb0 100644 --- a/src/version.ts +++ b/src/version.ts @@ -1,2 +1,2 @@ -export const LIB_NAME = "astra-db-ts"; -export const LIB_VERSION = "1.0.0-alpha.1"; +export const LIB_NAME = 'astra-db-ts'; +export const LIB_VERSION = "0.1.4"; diff --git a/tests/fixtures.ts b/tests/fixtures.ts index a662d3c2..ee10a910 100644 --- a/tests/fixtures.ts +++ b/tests/fixtures.ts @@ -37,6 +37,8 @@ export const initTestObjects = async (ctx: Context, useHttp2: boolean = true): P const coll = (!collCreated) ? await (async () => { + await db.dropCollection(EPHEMERAL_COLLECTION_NAME); + await db.dropCollection(EPHEMERAL_COLLECTION_NAME, { namespace: OTHER_NAMESPACE }); await db.createCollection(DEFAULT_COLLECTION_NAME, { vector: { dimension: 5, metric: 'cosine' }, checkExists: false, namespace: OTHER_NAMESPACE }); return await db.createCollection(DEFAULT_COLLECTION_NAME, { vector: { dimension: 5, metric: 'cosine' }, checkExists: false }) })() diff --git a/tests/integration/data-api/collection/misc.test.ts b/tests/integration/data-api/collection/misc.test.ts index a144a696..9c5449bb 100644 --- a/tests/integration/data-api/collection/misc.test.ts +++ b/tests/integration/data-api/collection/misc.test.ts @@ -12,6 +12,7 @@ import { Collection, DataAPITimeout, Db } from '@/src/data-api'; import { DEFAULT_COLLECTION_NAME, initTestObjects } from '@/tests/fixtures'; import assert from 'assert'; import { DEFAULT_NAMESPACE } from '@/src/api'; +import { CollectionNotFoundError } from '@/src/data-api/errors'; describe('integration.data-api.collection.misc', () => { let db: Db; @@ -61,4 +62,30 @@ describe('integration.data-api.collection.misc', () => { } }); }); + + describe('CollectionNotFoundError', () => { + it('is thrown when doing data api operation on non-existent collection', async () => { + const collection = db.collection('non_existent_collection'); + + try { + await collection.insertOne({ username: 'test' }); + } catch (e: any) { + assert.ok(e instanceof CollectionNotFoundError); + assert.strictEqual(e.namespace, DEFAULT_NAMESPACE); + assert.strictEqual(e.collectionName, 'non_existent_collection'); + } + }); + + it('is thrown when doing .options() on non-existent collection', async () => { + const collection = db.collection('non_existent_collection'); + + try { + await collection.options(); + } catch (e: any) { + assert.ok(e instanceof CollectionNotFoundError); + assert.strictEqual(e.namespace, DEFAULT_NAMESPACE); + assert.strictEqual(e.collectionName, 'non_existent_collection'); + } + }); + }); }); diff --git a/tests/integration/data-api/collection/options.test.ts b/tests/integration/data-api/collection/options.test.ts index ef4ccf88..33a33df4 100644 --- a/tests/integration/data-api/collection/options.test.ts +++ b/tests/integration/data-api/collection/options.test.ts @@ -32,9 +32,4 @@ describe('integration.data-api.collection.options', () => { assert.deepStrictEqual(res, {}); await db.dropCollection('test_db_collection_empty_opts') }); - - it('throws an error when collection not found', async () => { - const coll = db.collection('nonexistent_collection'); - await assert.rejects(coll.options(), /Collection 'nonexistent_collection' not found/); - }); }); diff --git a/tests/integration/data-api/cursor.test.ts b/tests/integration/data-api/cursor.test.ts index 841d5f03..995e784a 100644 --- a/tests/integration/data-api/cursor.test.ts +++ b/tests/integration/data-api/cursor.test.ts @@ -13,7 +13,7 @@ // limitations under the License. // noinspection DuplicatedCode -import { Collection, CursorAlreadyInitializedError, DataAPIResponseError, FindCursor } from '@/src/data-api'; +import { Collection, CursorIsStartedError, DataAPIResponseError, FindCursor } from '@/src/data-api'; import { initTestObjects } from '@/tests/fixtures'; import { DataAPIHttpClient } from '@/src/api'; import assert from 'assert'; @@ -114,7 +114,7 @@ describe('integration.data-api.cursor', async () => { assert.strictEqual(cursor.bufferedCount(), 3, 'Cursor did not set buffer'); await cursor.close(); - assert.throws(() => cursor.filter({ _id: '1' }), CursorAlreadyInitializedError); + assert.throws(() => cursor.filter({ _id: '1' }), CursorIsStartedError); const clone = cursor.clone(); clone.filter({ _id: '1' }); assert.deepStrictEqual(clone['_filter'], { _id: '1' }, 'Cursor did not set new filter'); diff --git a/tests/integration/data-api/db.test.ts b/tests/integration/data-api/db.test.ts index c01eaa00..136c069a 100644 --- a/tests/integration/data-api/db.test.ts +++ b/tests/integration/data-api/db.test.ts @@ -25,6 +25,7 @@ import { CollectionAlreadyExistsError, DataAPIResponseError, Db } from '@/src/da import { DEFAULT_DATA_API_PATH, DEFAULT_NAMESPACE } from '@/src/api'; import { DataAPIClient } from '@/src/client'; import process from 'process'; +import { CollectionNotFoundError } from '@/src/data-api/errors'; describe('integration.data-api.db', async () => { let db: Db; @@ -223,7 +224,7 @@ describe('integration.data-api.db', async () => { await db.command({ findOne: {} }, { collection: EPHEMERAL_COLLECTION_NAME }); assert.fail('Expected an error'); } catch (e) { - assert.ok(e instanceof DataAPIResponseError); + assert.ok(e instanceof CollectionNotFoundError); } }); }); diff --git a/tests/typing/collections/find/find-one.ts b/tests/typing/collections/find/find-one.ts index 28794a0b..ee3b2b6f 100644 --- a/tests/typing/collections/find/find-one.ts +++ b/tests/typing/collections/find/find-one.ts @@ -42,10 +42,10 @@ void dummyCollection().findOne({ { 'customer.age': { $gt: 18 } }, { 'customer.age': { $lt: 50 } }, { $not: { 'customer.name': { $in: ['John'] } } }, - { 'purchase_date': { $date: 123 } }, + { 'purchase_date': new Date(123) }, ], - 'purchase_date': { $gte: { $date: 123 } }, - 'items': { $gte: { $date: 123 } }, + 'purchase_date': { $gte: new Date(123) }, + 'items': { $gte: new Date(123) }, 'arr.0': { age: 3 }, 'arr.0.age': 3, }, { @@ -66,10 +66,10 @@ void dummyCollection().findOne({ { 'customer.age': { $gt: 18 } }, { 'customer.age': { $lt: 50 } }, { $not: { 'customer.name': { $in: ['John'] } } }, - { 'purchase_date': { $date: 123 } }, + { 'purchase_date': new Date(123) }, ], - 'purchase_date': { $gte: { $date: 123 } }, - 'items': { $gte: { $date: 123 } }, + 'purchase_date': { $gte: new Date(123) }, + 'items': { $gte: new Date(123) }, 'arr.0': { age: 3 }, } satisfies StrictFilter, { sort: { diff --git a/tests/typing/strict-filter.ts b/tests/typing/strict-filter.ts index fe8dc041..76b96afc 100644 --- a/tests/typing/strict-filter.ts +++ b/tests/typing/strict-filter.ts @@ -57,13 +57,12 @@ type test1 = Expect, { $lte?: number | bigint, $gt?: number | bigint, $gte?: number | bigint, - } & { - $lt?: Date | { $date: number }, - $lte?: Date | { $date: number }, - $gt?: Date | { $date: number }, - $gte?: Date | { $date: number }, - $date?: number, - }, + } & ({ + $lt?: Date, + $lte?: Date, + $gt?: Date, + $gte?: Date, + } | Date) $and?: StrictFilter[], $or?: StrictFilter[], $not?: StrictFilter, diff --git a/tests/typing/strict-update-filter.ts b/tests/typing/strict-update-filter.ts index 23e45b06..bcd0365d 100644 --- a/tests/typing/strict-update-filter.ts +++ b/tests/typing/strict-update-filter.ts @@ -14,7 +14,7 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -import type { SomeDoc } from '@/src/data-api'; +import { CurrentDate, SomeDoc, ToDotNotation } from '@/src/data-api'; import type { StrictUpdateFilter } from '@/src/data-api/types'; import type { BasicSchema, ConvolutedSchema1, ConvolutedSchema2, Equal, Expect, Schema } from '@/tests/typing/prelude'; @@ -64,7 +64,7 @@ type test1 = Expect, { 'obj.str': string, obj: string, }>, - $currentDate?: Partial>, + $currentDate?: CurrentDate>, $min?: Partial<{ num: number | bigint, [k: `any.${string}`]: number | bigint, diff --git a/tests/unit/data-api/cursor.test.ts b/tests/unit/data-api/cursor.test.ts index 58fb5058..8c3ad426 100644 --- a/tests/unit/data-api/cursor.test.ts +++ b/tests/unit/data-api/cursor.test.ts @@ -14,7 +14,7 @@ // noinspection DuplicatedCode import assert from 'assert'; -import { FindCursor, CursorAlreadyInitializedError } from '@/src/data-api'; +import { FindCursor, CursorIsStartedError } from '@/src/data-api'; import { DataAPIHttpClient } from '@/src/api'; describe('unit.data-api.cursor', async () => { @@ -66,7 +66,7 @@ describe('unit.data-api.cursor', async () => { it('should fail setting filter if cursor is not uninitialized', async () => { const cursor = new FindCursor('test_keyspace', httpClient, {}); await cursor.close(); - assert.throws(() => cursor.filter({ _id: '1' }), CursorAlreadyInitializedError); + assert.throws(() => cursor.filter({ _id: '1' }), CursorIsStartedError); }); it('should set new sort', async () => { @@ -78,7 +78,7 @@ describe('unit.data-api.cursor', async () => { it('should fail setting sort if cursor is not uninitialized', async () => { const cursor = new FindCursor('test_keyspace', httpClient, {}); await cursor.close(); - assert.throws(() => cursor.sort({ _id: 1 }), CursorAlreadyInitializedError); + assert.throws(() => cursor.sort({ _id: 1 }), CursorIsStartedError); }); it('should set new limit', async () => { @@ -90,7 +90,7 @@ describe('unit.data-api.cursor', async () => { it('should fail setting limit if cursor is not uninitialized', async () => { const cursor = new FindCursor('test_keyspace', httpClient, {}); await cursor.close(); - assert.throws(() => cursor.limit(10), CursorAlreadyInitializedError); + assert.throws(() => cursor.limit(10), CursorIsStartedError); }); it('should set new skip', async () => { @@ -102,7 +102,7 @@ describe('unit.data-api.cursor', async () => { it('should fail setting skip if cursor is not uninitialized', async () => { const cursor = new FindCursor('test_keyspace', httpClient, {}); await cursor.close(); - assert.throws(() => cursor.skip(5), CursorAlreadyInitializedError); + assert.throws(() => cursor.skip(5), CursorIsStartedError); }); it('should set new projection', async () => { @@ -114,7 +114,7 @@ describe('unit.data-api.cursor', async () => { it('should fail setting projection if cursor is not uninitialized', async () => { const cursor = new FindCursor('test_keyspace', httpClient, {}); await cursor.close(); - assert.throws(() => cursor.project({ _id: 0 }), CursorAlreadyInitializedError); + assert.throws(() => cursor.project({ _id: 0 }), CursorIsStartedError); }); it('should set new includeSimilarity', async () => { @@ -132,7 +132,7 @@ describe('unit.data-api.cursor', async () => { it('should fail setting includeSimilarity if cursor is not uninitialized', async () => { const cursor = new FindCursor('test_keyspace', httpClient, {}); await cursor.close(); - assert.throws(() => cursor.includeSimilarity(true), CursorAlreadyInitializedError); + assert.throws(() => cursor.includeSimilarity(true), CursorIsStartedError); }); it('should set new mapping', async () => { @@ -150,7 +150,7 @@ describe('unit.data-api.cursor', async () => { it('should fail setting mapping if cursor is not uninitialized', async () => { const cursor = new FindCursor('test_keyspace', httpClient, {}); await cursor.close(); - assert.throws(() => cursor.map(add1), CursorAlreadyInitializedError); + assert.throws(() => cursor.map(add1), CursorIsStartedError); }); }); }); diff --git a/tests/unit/data-api/errors.test.ts b/tests/unit/data-api/errors.test.ts index b770acf3..1f2265ab 100644 --- a/tests/unit/data-api/errors.test.ts +++ b/tests/unit/data-api/errors.test.ts @@ -64,7 +64,7 @@ describe('unit.data-api.errors', () => { }); describe('InsertManyError construction', () => { - const partialResult: InsertManyResult = { insertedIds: ['1', '2'], insertedCount: 2 }; + const partialResult: InsertManyResult = { insertedIds: ['1', '2'], insertedCount: 2 }; it('should properly construct a single-response InsertManyError', () => { const err = mkRespErrorFromResponse(InsertManyError, commands[0], raws[0], partialResult); diff --git a/tests/unit/data-api/ids.test.ts b/tests/unit/data-api/ids.test.ts index 8c65c6e6..409e3679 100644 --- a/tests/unit/data-api/ids.test.ts +++ b/tests/unit/data-api/ids.test.ts @@ -15,7 +15,6 @@ import assert from 'assert'; import { ObjectId, UUID } from '@/src/data-api'; -import { BSONError } from 'bson'; describe('unit.data-api.ids', () => { describe('UUID', () => { @@ -102,6 +101,11 @@ describe('unit.data-api.ids', () => { const uuid = new UUID('123e4567-e89b-12d3-a456-426614174000'); assert(!uuid.equals({})); }); + + it('should properly serialize to JSON', () => { + const uuid = new UUID('123e4567-e89b-12d3-a456-426614174000'); + assert.strictEqual(JSON.stringify(uuid), '{"$uuid":"123e4567-e89b-12d3-a456-426614174000"}'); + }); }); describe('ObjectId', () => { @@ -119,11 +123,11 @@ describe('unit.data-api.ids', () => { }); it('should "allow" force construction on invalid ObjectId', () => { - assert.throws(() => new ObjectId('507f191e810c19729de860e', false), BSONError); + assert.throws(() => new ObjectId('507f191e810c19729de860e', false), Error); }); it('should "allow" force construction on invalid type', () => { - assert.throws(() => new ObjectId({} as any, false), BSONError); + assert.throws(() => new ObjectId({} as any, false), Error); }); it('should properly parse an ObjectId', () => { @@ -164,56 +168,10 @@ describe('unit.data-api.ids', () => { const objectId = new ObjectId('507f191e810c19729de860ea'); assert(!objectId.equals({})); }); - }); - // describe('replaceRawId', () => { - // it('should return null for null', () => { - // assert.strictEqual(replaceRawId(null), null); - // }); - // - // it('should return same id if not special id', () => { - // assert.strictEqual(replaceRawId('some_id'), 'some_id'); - // }); - // - // it('should return UUID if $uuid', () => { - // const id = { $uuid: '123e4567-e89b-12d3-a456-426614174000' }; - // const replaced = replaceRawId(id); - // assert(replaced instanceof UUID); - // assert.strictEqual(replaced.toString(), '123e4567-e89b-12d3-a456-426614174000'); - // }); - // - // it('should return ObjectId if $objectId', () => { - // const id = { $objectId: '507f191e810c19729de860ea' }; - // const replaced = replaceRawId(id); - // assert(replaced instanceof ObjectId); - // assert.strictEqual(replaced.toString(), '507f191e810c19729de860ea'); - // }); - // - // it('should return same id if not special id _id', () => { - // const id = { _id: 'some_id' }; - // const replaced = replaceRawId(id); - // assert.strictEqual(replaced._id, 'some_id'); - // }); - // - // it('should return UUID if $uuid _id', () => { - // const id = { _id: { $uuid: '123e4567-e89b-12d3-a456-426614174000' } }; - // const replaced = replaceRawId(id); - // assert(replaced._id instanceof UUID); - // assert.strictEqual(replaced._id.toString(), '123e4567-e89b-12d3-a456-426614174000'); - // }); - // - // it('should return ObjectId if $objectId _id', () => { - // const id = { _id: { $objectId: '507f191e810c19729de860ea' } }; - // const replaced = replaceRawId(id); - // assert(replaced._id instanceof ObjectId); - // assert.strictEqual(replaced._id.toString(), '507f191e810c19729de860ea'); - // }); - // - // it('Mutates the original object if on _id', () => { - // const id = { _id: { $uuid: '123e4567-e89b-12d3-a456-426614174000' } }; - // replaceRawId(id); - // assert(id._id instanceof UUID); - // assert.strictEqual(id._id.toString(), '123e4567-e89b-12d3-a456-426614174000'); - // }); - // }); + it('should properly serialize to JSON', () => { + const objectId = new ObjectId('507f191e810c19729de860ea'); + assert.strictEqual(JSON.stringify(objectId), '{"$objectId":"507f191e810c19729de860ea"}'); + }); + }); }); diff --git a/tests/unit/devops/errors.test.ts b/tests/unit/devops/errors.test.ts index 6bff279f..4f332124 100644 --- a/tests/unit/devops/errors.test.ts +++ b/tests/unit/devops/errors.test.ts @@ -18,33 +18,31 @@ import { DevOpsAPIResponseError } from '@/src/devops'; describe('unit.devops.errors', () => { describe('DevOpsAPIResponseError construction', () => { - it('should properly construct a DataAPIResponseError with no underlying errors given', () => { + it('should properly construct a DevOpsAPIResponseError with no underlying errors given', () => { const rootError = { message: 'Something went wrong' } as any; const err = new DevOpsAPIResponseError(rootError); assert.strictEqual(err.message, 'Something went wrong'); assert.deepStrictEqual(err.errors, []); assert.strictEqual(err.status, undefined); - assert.deepStrictEqual(err.rootError, rootError); assert.strictEqual(err.name, 'DevOpsAPIResponseError') }); - it('should properly construct a DataAPIResponseError with underlying errors', () => { + it('should properly construct a DevOpsAPIResponseError with underlying errors', () => { const rootError = { message: 'Something went wrong', response: { data: { errors: [ - { message: 'Error 1' }, - { message: 'Error 2' }, + { ID: 1, message: 'Error 1' }, + { ID: 2 }, ], }, }, } as any; const err = new DevOpsAPIResponseError(rootError); assert.strictEqual(err.message, 'Error 1'); - assert.deepStrictEqual(err.errors, [{ message: 'Error 1' }, { message: 'Error 2' }]); + assert.deepStrictEqual(err.errors, [{ id: 1, message: 'Error 1' }, { id: 2, message: undefined }]); assert.strictEqual(err.status, undefined); - assert.deepStrictEqual(err.rootError, rootError); assert.strictEqual(err.name, 'DevOpsAPIResponseError') }); });