diff --git a/examples/web/vector-index/.eslintignore b/examples/web/vector-index/.eslintignore new file mode 100644 index 000000000..0564a5a74 --- /dev/null +++ b/examples/web/vector-index/.eslintignore @@ -0,0 +1,3 @@ +node_modules +dist +**/*.d.ts diff --git a/examples/web/vector-index/.eslintrc.json b/examples/web/vector-index/.eslintrc.json new file mode 100644 index 000000000..7f81c1af6 --- /dev/null +++ b/examples/web/vector-index/.eslintrc.json @@ -0,0 +1,62 @@ +{ + "root": true, + "env": { + "es2021": true + }, + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:@typescript-eslint/recommended-requiring-type-checking", + "plugin:import/recommended", + "plugin:prettier/recommended", + "plugin:node/recommended" + ], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": 12, + "project": "./tsconfig.json" + }, + "plugins": ["@typescript-eslint"], + "rules": { + "semi": ["error", "always"], + "import/no-extraneous-dependencies": ["error", {}], + "node/no-unsupported-features/es-syntax": "off", + "node/no-missing-import": [ + "error", + { + "tryExtensions": [".js", ".ts", ".json", ".node"] + } + ], + "prettier/prettier": "error", + "block-scoped-var": "error", + "eqeqeq": "error", + "no-var": "error", + "prefer-const": "error", + "eol-last": "error", + "prefer-arrow-callback": "error", + "no-trailing-spaces": "error", + "quotes": ["warn", "single", {"avoidEscape": true}], + "no-restricted-properties": [ + "error", + { + "object": "describe", + "property": "only" + }, + { + "object": "it", + "property": "only" + } + ], + // async without await is often an error and in other uses it obfuscates + // the intent of the developer. Functions are async when they want to await. + "require-await": "error", + "import/no-duplicates": "error" + }, + "settings": { + "import/resolver": { + "node": { + "extensions": [".js", ".jsx", ".ts", ".tsx"] + } + } + } +} diff --git a/examples/web/vector-index/.prettierrc.json b/examples/web/vector-index/.prettierrc.json new file mode 100644 index 000000000..a3b729076 --- /dev/null +++ b/examples/web/vector-index/.prettierrc.json @@ -0,0 +1,8 @@ +{ + "bracketSpacing": false, + "singleQuote": true, + "trailingComma": "es5", + "arrowParens": "avoid", + "printWidth": 120 + } + diff --git a/examples/web/vector-index/doc-example-files/doc-examples-web-apis.ts b/examples/web/vector-index/doc-example-files/doc-examples-web-apis.ts index a6d28b5e9..f50223a5f 100644 --- a/examples/web/vector-index/doc-example-files/doc-examples-web-apis.ts +++ b/examples/web/vector-index/doc-example-files/doc-examples-web-apis.ts @@ -2,10 +2,14 @@ import { CreateVectorIndex, DeleteVectorIndex, ListVectorIndexes, - PreviewVectorIndexClient, VectorSearch, - VectorUpsertItemBatch -} from "@gomomento/sdk-web"; -import {ALL_VECTOR_METADATA} from "@gomomento/sdk-core"; + PreviewVectorIndexClient, + VectorSearch, + VectorUpsertItemBatch, + ALL_VECTOR_METADATA, + Configurations, + CredentialProvider, +} from '@gomomento/sdk-web'; +import {initJSDom} from '../utils/jsdom'; async function example_API_CreateIndex(vectorClient: PreviewVectorIndexClient) { const result = await vectorClient.createIndex('test-index', 2); @@ -23,18 +27,14 @@ async function example_API_CreateIndex(vectorClient: PreviewVectorIndexClient) { async function example_API_ListIndexes(vectorClient: PreviewVectorIndexClient) { const result = await vectorClient.listIndexes(); if (result instanceof ListVectorIndexes.Success) { - console.log( - `Indexes:\n${result - .getIndexNames() - .join('\n')}\n\n` - ); + console.log(`Indexes:\n${result.getIndexNames().join('\n')}\n\n`); } else if (result instanceof ListVectorIndexes.Error) { throw new Error(`An error occurred while attempting to list caches: ${result.errorCode()}: ${result.toString()}`); } } async function example_API_DeleteIndex(vectorClient: PreviewVectorIndexClient) { - const result = await vectorClient.deleteIndex('test-index') + const result = await vectorClient.deleteIndex('test-index'); if (result instanceof DeleteVectorIndex.Success) { console.log("Index 'test-index' deleted"); } else if (result instanceof DeleteVectorIndex.Error) { @@ -81,3 +81,23 @@ async function example_API_Search(vectorClient: PreviewVectorIndexClient) { throw new Error(`An error occurred searching index test-index: ${result.errorCode()}: ${result.toString()}`); } } + +async function main() { + // Because the Momento Web SDK is intended for use in a browser, we use the JSDom library to set up an environment + // that will allow us to use it in a node.js program. + initJSDom(); + const vectorClient = new PreviewVectorIndexClient({ + configuration: Configurations.Laptop.latest(), + credentialProvider: CredentialProvider.fromEnvironmentVariable({environmentVariableName: 'MOMENTO_API_KEY'}), + }); + await example_API_CreateIndex(vectorClient); + await example_API_ListIndexes(vectorClient); + await example_API_UpsertItemBatch(vectorClient); + await example_API_Search(vectorClient); + await example_API_DeleteItemBatch(vectorClient); + await example_API_DeleteIndex(vectorClient); +} + +main().catch(e => { + throw e; +}); diff --git a/examples/web/vector-index/package-lock.json b/examples/web/vector-index/package-lock.json index d951218e0..e237af6bc 100644 --- a/examples/web/vector-index/package-lock.json +++ b/examples/web/vector-index/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "@gomomento/sdk-web": "^1.41.0", + "@gomomento/sdk-web": "^1.45.0", "jsdom": "22.1.0" }, "devDependencies": { @@ -82,18 +82,18 @@ } }, "node_modules/@gomomento/generated-types-webtext": { - "version": "0.84.0", - "resolved": "https://registry.npmjs.org/@gomomento/generated-types-webtext/-/generated-types-webtext-0.84.0.tgz", - "integrity": "sha512-umoxs01o8a/BsqmoqdxyQBfWJsU7wmure0nQ7fBc1j6Km6XnBwXjK+Ut16MQwmISf3GNTWvmKGepCVexnEPZaA==", + "version": "0.87.0", + "resolved": "https://registry.npmjs.org/@gomomento/generated-types-webtext/-/generated-types-webtext-0.87.0.tgz", + "integrity": "sha512-cXTwTaf7JFAolTjS78tSgZv+Ci/+7B6ZlPnD86wJeNUe3F1fuhz8auxQ7R75zOIXQX/0Xzj/0NQRhnIPnhEErw==", "dependencies": { "google-protobuf": "3.21.2", "grpc-web": "1.4.2" } }, "node_modules/@gomomento/sdk-core": { - "version": "1.41.0", - "resolved": "https://registry.npmjs.org/@gomomento/sdk-core/-/sdk-core-1.41.0.tgz", - "integrity": "sha512-/WHrVJ5dpFom9F35JDjiiwGJ5ZoB0HXEL2Zal7WT8/qOTYEjKClAYjJJ5guIiO/875YGgutz5MsPniftMFsy6g==", + "version": "1.45.0", + "resolved": "https://registry.npmjs.org/@gomomento/sdk-core/-/sdk-core-1.45.0.tgz", + "integrity": "sha512-FphHhFXDTsPGOlcE2YOyPrM9D6Tl5LuWyP6Ofb4KmSTExGKPLOyvHMKZClB7caHdP+wzFBF2PG4oCbIhVoc/JQ==", "dependencies": { "buffer": "^6.0.3", "jwt-decode": "3.1.2" @@ -103,12 +103,12 @@ } }, "node_modules/@gomomento/sdk-web": { - "version": "1.41.0", - "resolved": "https://registry.npmjs.org/@gomomento/sdk-web/-/sdk-web-1.41.0.tgz", - "integrity": "sha512-TirOSdY/hfXX0X+hD0B0K5lJhfzL36DEW71HJueHu5h6OQiOuUFzuptzlFbEmgJYTxyak0/HNpjCvURCqpLlSQ==", + "version": "1.45.0", + "resolved": "https://registry.npmjs.org/@gomomento/sdk-web/-/sdk-web-1.45.0.tgz", + "integrity": "sha512-p6ZFPpPJcNJkJPe2XZLl/QtgcaCea7TvXKw4Ay1Sr7+wAk73q9LLLRfH3wlNZs3xGIYTY/HetL5rvLzCOumFHw==", "dependencies": { - "@gomomento/generated-types-webtext": "0.84.0", - "@gomomento/sdk-core": "1.41.0", + "@gomomento/generated-types-webtext": "0.87.0", + "@gomomento/sdk-core": "1.45.0", "google-protobuf": "3.21.2", "grpc-web": "1.4.2", "jwt-decode": "3.1.2" @@ -3308,30 +3308,30 @@ } }, "@gomomento/generated-types-webtext": { - "version": "0.84.0", - "resolved": "https://registry.npmjs.org/@gomomento/generated-types-webtext/-/generated-types-webtext-0.84.0.tgz", - "integrity": "sha512-umoxs01o8a/BsqmoqdxyQBfWJsU7wmure0nQ7fBc1j6Km6XnBwXjK+Ut16MQwmISf3GNTWvmKGepCVexnEPZaA==", + "version": "0.87.0", + "resolved": "https://registry.npmjs.org/@gomomento/generated-types-webtext/-/generated-types-webtext-0.87.0.tgz", + "integrity": "sha512-cXTwTaf7JFAolTjS78tSgZv+Ci/+7B6ZlPnD86wJeNUe3F1fuhz8auxQ7R75zOIXQX/0Xzj/0NQRhnIPnhEErw==", "requires": { "google-protobuf": "3.21.2", "grpc-web": "1.4.2" } }, "@gomomento/sdk-core": { - "version": "1.41.0", - "resolved": "https://registry.npmjs.org/@gomomento/sdk-core/-/sdk-core-1.41.0.tgz", - "integrity": "sha512-/WHrVJ5dpFom9F35JDjiiwGJ5ZoB0HXEL2Zal7WT8/qOTYEjKClAYjJJ5guIiO/875YGgutz5MsPniftMFsy6g==", + "version": "1.45.0", + "resolved": "https://registry.npmjs.org/@gomomento/sdk-core/-/sdk-core-1.45.0.tgz", + "integrity": "sha512-FphHhFXDTsPGOlcE2YOyPrM9D6Tl5LuWyP6Ofb4KmSTExGKPLOyvHMKZClB7caHdP+wzFBF2PG4oCbIhVoc/JQ==", "requires": { "buffer": "^6.0.3", "jwt-decode": "3.1.2" } }, "@gomomento/sdk-web": { - "version": "1.41.0", - "resolved": "https://registry.npmjs.org/@gomomento/sdk-web/-/sdk-web-1.41.0.tgz", - "integrity": "sha512-TirOSdY/hfXX0X+hD0B0K5lJhfzL36DEW71HJueHu5h6OQiOuUFzuptzlFbEmgJYTxyak0/HNpjCvURCqpLlSQ==", + "version": "1.45.0", + "resolved": "https://registry.npmjs.org/@gomomento/sdk-web/-/sdk-web-1.45.0.tgz", + "integrity": "sha512-p6ZFPpPJcNJkJPe2XZLl/QtgcaCea7TvXKw4Ay1Sr7+wAk73q9LLLRfH3wlNZs3xGIYTY/HetL5rvLzCOumFHw==", "requires": { - "@gomomento/generated-types-webtext": "0.84.0", - "@gomomento/sdk-core": "1.41.0", + "@gomomento/generated-types-webtext": "0.87.0", + "@gomomento/sdk-core": "1.45.0", "google-protobuf": "3.21.2", "grpc-web": "1.4.2", "jwt-decode": "3.1.2" diff --git a/examples/web/vector-index/package.json b/examples/web/vector-index/package.json index 74533032d..fd3eafe7f 100644 --- a/examples/web/vector-index/package.json +++ b/examples/web/vector-index/package.json @@ -6,7 +6,7 @@ "scripts": { "prebuild": "eslint . --ext .ts", "build": "tsc", - "validate-examples": "tsc && node dist/doc-examples-web-apis.js", + "validate-examples": "tsc && node dist/doc-example-files/doc-examples-web-apis.js", "test": "jest", "lint": "eslint . --ext .ts", "format": "eslint . --ext .ts --fix" @@ -26,7 +26,7 @@ "typescript": "4.4.3" }, "dependencies": { - "@gomomento/sdk-web": "^1.41.0", + "@gomomento/sdk-web": "^1.45.0", "jsdom": "22.1.0" } } diff --git a/examples/web/vector-index/tsconfig.json b/examples/web/vector-index/tsconfig.json new file mode 100644 index 000000000..ecae69dce --- /dev/null +++ b/examples/web/vector-index/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "target": "ES2021", + "module": "commonjs", + "lib": [ + "es2021", + ], + "declaration": true, + "strict": true, + "noImplicitAny": true, + "strictNullChecks": true, + "noImplicitThis": true, + "alwaysStrict": true, + "noUnusedLocals": false, + "noUnusedParameters": false, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": false, + "inlineSourceMap": true, + "inlineSources": true, + "experimentalDecorators": true, + "strictPropertyInitialization": false, + "typeRoots": [ + "./node_modules/@types" + ], + "outDir": "./dist" + } +} diff --git a/examples/web/vector-index/utils/jsdom.ts b/examples/web/vector-index/utils/jsdom.ts new file mode 100644 index 000000000..9616cbd9d --- /dev/null +++ b/examples/web/vector-index/utils/jsdom.ts @@ -0,0 +1,61 @@ +/* eslint-disable @typescript-eslint/no-var-requires,@typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-return */ +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-nocheck + +const JSDOM = require('jsdom'); + +const defaultHtml = '
'; + +export function initJSDom(html = defaultHtml, options = {}) { + // Idempotency + if ( + global.navigator && + global.navigator.userAgent && + global.navigator.userAgent.includes('Node.js') && + global.document && + typeof global.document.destroy === 'function' + ) { + return global.document.destroy; + } + + if (!('url' in options)) { + Object.assign(options, {url: 'http://localhost:3000'}); + } + + // enable pretendToBeVisual by default since react needs + // window.requestAnimationFrame, see https://github.com/jsdom/jsdom#pretending-to-be-a-visual-browser + if (!('pretendToBeVisual' in options)) { + Object.assign(options, {pretendToBeVisual: true}); + } + + const jsdom = new JSDOM.JSDOM(html, options); + const {window} = jsdom; + const {document} = window; + + // generate our list of keys by enumerating document.window - this list may vary + // based on the jsdom version. filter out internal methods as well as anything + // that node already defines + + const KEYS = []; + + if (KEYS.length === 0) { + KEYS.push(...Object.getOwnPropertyNames(window).filter(k => !k.startsWith('_') && !(k in global))); + // going to add our jsdom instance, see below + KEYS.push('$jsdom'); + } + KEYS.forEach(key => (global[key] = window[key])); + + // setup document / window / window.console + global.document = document; + global.window = window; + window.console = global.console; + + // add access to our jsdom instance + global.$jsdom = jsdom; + + const cleanup = () => KEYS.forEach(key => delete global[key]); + + document.destroy = cleanup; + + return cleanup; +}