From d1ef112ea3576c3e7678e42f85d25fb0356bfa1e Mon Sep 17 00:00:00 2001 From: DidaS Date: Fri, 23 Jun 2023 19:30:33 +0100 Subject: [PATCH 1/4] Changed version This change was made so the draft pull request can be open --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 3a6a3b7..55d3760 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redis-om", - "version": "0.4.0-beta.3", + "version": "0.0.0", "description": "Object mapping, and more, for Redis and Node.js. Written in TypeScript.", "main": "dist/index.js", "types": "./dist/index.d.ts", @@ -58,4 +58,4 @@ "redis": "^4.6.4", "ulid": "^2.3.0" } -} +} \ No newline at end of file From 8d1ae097fd323e4a25860cbdb03ea3f4e21d4216 Mon Sep 17 00:00:00 2001 From: DidaS Date: Sun, 27 Aug 2023 15:40:51 +0100 Subject: [PATCH 2/4] Merge `nekdis v0.10.1` -> `redis-om` This is experimental and the PR is still a draft --- .gitattributes | 2 + .gitignore | 10 +- .nvmrc | 1 - docs/.nojekyll | 1 - docs/README.md | 466 -- docs/classes/AbstractSearch.md | 912 ---- docs/classes/ArrayHashInput.md | 198 - docs/classes/Circle.md | 375 -- docs/classes/Client.md | 190 - docs/classes/Field.md | 237 - docs/classes/FieldNotInSchema.md | 198 - docs/classes/InvalidHashInput.md | 213 - docs/classes/InvalidHashValue.md | 228 - docs/classes/InvalidInput.md | 207 - docs/classes/InvalidJsonInput.md | 228 - docs/classes/InvalidJsonValue.md | 228 - docs/classes/InvalidSchema.md | 197 - docs/classes/InvalidValue.md | 203 - docs/classes/NestedHashInput.md | 198 - docs/classes/NullJsonInput.md | 228 - docs/classes/NullJsonValue.md | 228 - docs/classes/PointOutOfRange.md | 203 - docs/classes/RawSearch.md | 1063 ---- docs/classes/RedisOmError.md | 207 - docs/classes/Repository.md | 380 -- docs/classes/Schema.md | 242 - docs/classes/Search.md | 1199 ----- docs/classes/SearchError.md | 201 - docs/classes/SemanticSearchError.md | 197 - docs/classes/Where.md | 43 - docs/classes/WhereField.md | 909 ---- examples/table-module.ts | 142 + lib/client/client.ts | 214 - lib/client/index.ts | 1 - lib/entity/entity.ts | 31 - lib/entity/index.ts | 1 - lib/error/index.ts | 6 - lib/error/invalid-input.ts | 74 - lib/error/invalid-schema.ts | 3 - lib/error/invalid-value.ts | 49 - lib/error/point-out-of-range.ts | 18 - lib/error/redis-om-error.ts | 1 - lib/error/search-error.ts | 20 - lib/index.ts | 11 - lib/indexer/index-builder.ts | 156 - lib/indexer/index.ts | 1 - lib/repository/index.ts | 1 - lib/repository/repository.ts | 341 -- lib/schema/definitions.ts | 118 - lib/schema/field.ts | 83 - lib/schema/index.ts | 4 - lib/schema/options.ts | 52 - lib/schema/schema.ts | 165 - lib/search/index.ts | 13 - lib/search/results-converter.ts | 61 - lib/search/search.ts | 613 --- lib/search/where-and.ts | 16 - lib/search/where-boolean.ts | 32 - lib/search/where-date.ts | 84 - lib/search/where-field.ts | 316 -- lib/search/where-number.ts | 70 - lib/search/where-or.ts | 16 - lib/search/where-point.ts | 192 - lib/search/where-string-array.ts | 26 - lib/search/where-string.ts | 39 - lib/search/where-text.ts | 52 - lib/search/where.ts | 9 - lib/transformer/from-hash-transformer.ts | 40 - lib/transformer/from-json-transformer.ts | 70 - lib/transformer/index.ts | 4 - lib/transformer/to-hash-transformer.ts | 59 - lib/transformer/to-json-transformer.ts | 96 - lib/transformer/transformer-common.ts | 64 - package-lock.json | 4356 ----------------- package.json | 69 +- pnpm-lock.yaml | 2674 ++++++++++ spec/constants.ts | 97 + spec/functional/core/fetch-hash.spec.ts | 56 - spec/functional/core/fetch-json.spec.ts | 54 - spec/functional/core/remove-hash.spec.ts | 70 - spec/functional/core/remove-json.spec.ts | 70 - spec/functional/core/save-hash.spec.ts | 56 - spec/functional/core/save-json.spec.ts | 55 - spec/functional/core/update-hash.spec.ts | 103 - spec/functional/core/update-json.spec.ts | 85 - spec/functional/demo.spec.ts | 167 - spec/functional/helpers/data-helper.ts | 47 - spec/functional/helpers/hash-example-data.ts | 141 - spec/functional/helpers/json-example-data.ts | 157 - spec/functional/helpers/redis-helper.ts | 47 - .../create-and-drop-index-on-hash.spec.ts | 118 - .../create-and-drop-index-on-json.spec.ts | 118 - .../search/drop-missing-index.spec.ts | 36 - spec/functional/search/search-hash.spec.ts | 219 - spec/functional/search/search-json.spec.ts | 206 - spec/helpers/custom-matchers.ts | 38 - spec/helpers/example-data.ts | 63 - spec/schema/array.spec.ts | 28 + spec/schema/boolean.spec.ts | 24 + spec/schema/date.spec.ts | 24 + spec/schema/number.spec.ts | 24 + spec/schema/object.spec.ts | 90 + spec/schema/parser.spec.ts | 10 + spec/schema/point.spec.ts | 24 + spec/schema/string.spec.ts | 24 + spec/schema/text.spec.ts | 24 + spec/tsconfig.json | 8 + spec/unit/client/client-close.spec.ts | 39 - spec/unit/client/client-create-index.spec.ts | 55 - spec/unit/client/client-drop-index.spec.ts | 42 - spec/unit/client/client-expire.spec.ts | 41 - .../client/client-fetch-repository.spec.ts | 61 - spec/unit/client/client-get-set.spec.ts | 92 - spec/unit/client/client-hgetall.spec.ts | 47 - spec/unit/client/client-hsetall.spec.ts | 45 - spec/unit/client/client-jsonget.spec.ts | 47 - spec/unit/client/client-jsonset.spec.ts | 41 - spec/unit/client/client-open.spec.ts | 89 - spec/unit/client/client-search.spec.ts | 46 - spec/unit/client/client-unlink.spec.ts | 51 - spec/unit/client/client-use.spec.ts | 63 - spec/unit/error/errors.spec.ts | 63 - spec/unit/helpers/mock-client.ts | 27 - spec/unit/helpers/mock-indexer.ts | 6 - spec/unit/helpers/mock-redis.ts | 36 - spec/unit/helpers/search-helpers.ts | 323 -- spec/unit/helpers/test-entity-and-schema.ts | 82 - spec/unit/indexer/boolean-hash-fields.spec.ts | 67 - spec/unit/indexer/boolean-json-fields.spec.ts | 97 - spec/unit/indexer/date-hash-fields.spec.ts | 67 - spec/unit/indexer/date-json-fields.spec.ts | 67 - spec/unit/indexer/index-builder.spec.ts | 103 - spec/unit/indexer/numeric-hash-fields.spec.ts | 67 - spec/unit/indexer/numeric-json-fields.spec.ts | 67 - spec/unit/indexer/point-hash-fields.spec.ts | 55 - spec/unit/indexer/point-json-fields.spec.ts | 56 - .../indexer/string-array-hash-fields.spec.ts | 61 - .../indexer/string-array-json-fields.spec.ts | 55 - spec/unit/indexer/string-hash-fields.spec.ts | 79 - spec/unit/indexer/string-json-fields.spec.ts | 111 - spec/unit/indexer/text-hash-fields.spec.ts | 91 - spec/unit/indexer/text-json-fields.spec.ts | 103 - .../repository/repository-constructor.spec.ts | 45 - .../repository-create-index.spec.ts | 145 - .../repository/repository-drop-index.spec.ts | 48 - .../unit/repository/repository-expire.spec.ts | 30 - spec/unit/repository/repository-fetch.spec.ts | 142 - .../unit/repository/repository-remove.spec.ts | 42 - spec/unit/repository/repository-save.spec.ts | 177 - .../unit/repository/repository-search.spec.ts | 67 - spec/unit/schema/field.spec.ts | 88 - spec/unit/schema/schema-index-hash.spec.ts | 83 - spec/unit/schema/schema.spec.ts | 159 - spec/unit/search/raw-search-query.spec.ts | 37 - spec/unit/search/search-by-boolean.spec.ts | 92 - spec/unit/search/search-by-date.spec.ts | 120 - spec/unit/search/search-by-number.spec.ts | 91 - spec/unit/search/search-by-point.spec.ts | 90 - .../search/search-by-string-array.spec.ts | 66 - spec/unit/search/search-by-string.spec.ts | 104 - spec/unit/search/search-by-text.spec.ts | 140 - spec/unit/search/search-query.spec.ts | 281 -- .../unit/search/search-return-all-ids.spec.ts | 117 - .../search/search-return-all-keys.spec.ts | 117 - spec/unit/search/search-return-all.spec.ts | 217 - spec/unit/search/search-return-count.spec.ts | 62 - .../search/search-return-first-id.spec.ts | 60 - .../search/search-return-first-key.spec.ts | 60 - spec/unit/search/search-return-first.spec.ts | 110 - spec/unit/search/search-return-max-id.spec.ts | 64 - .../unit/search/search-return-max-key.spec.ts | 65 - spec/unit/search/search-return-max.spec.ts | 115 - spec/unit/search/search-return-min-id.spec.ts | 64 - .../unit/search/search-return-min-key.spec.ts | 64 - spec/unit/search/search-return-min.spec.ts | 116 - .../search/search-return-page-of-ids.spec.ts | 89 - .../search/search-return-page-of-keys.spec.ts | 89 - spec/unit/search/search-return-page.spec.ts | 156 - spec/unit/search/search-sort-by.spec.ts | 365 -- spec/unit/transformer/from-redis-hash.spec.ts | 121 - spec/unit/transformer/from-redis-json.spec.ts | 198 - spec/unit/transformer/to-redis-hash.spec.ts | 180 - spec/unit/transformer/to-redis-json.spec.ts | 261 - src/client.ts | 134 + .../document-helpers/general-helpers.ts | 161 + src/document/document-helpers/hash-helpers.ts | 173 + src/document/document-helpers/index.ts | 3 + src/document/document-helpers/json-helpers.ts | 122 + src/document/hash-document.ts | 209 + src/document/index.ts | 2 + src/document/json-document.ts | 179 + src/index.ts | 3 + src/model.ts | 345 ++ src/schema.ts | 225 + src/search/search-builders/base.ts | 46 + src/search/search-builders/boolean.ts | 33 + src/search/search-builders/date.ts | 100 + src/search/search-builders/index.ts | 8 + src/search/search-builders/number.ts | 73 + src/search/search-builders/point.ts | 137 + src/search/search-builders/string.ts | 45 + src/search/search-builders/text.ts | 91 + src/search/search-builders/vector.ts | 80 + src/search/search.ts | 368 ++ src/typings/client.ts | 21 + src/typings/doc-base.ts | 3 + src/typings/extract-generic.ts | 5 + src/typings/field-map.ts | 19 + src/typings/index.ts | 18 + src/typings/map-schema.ts | 52 + src/typings/map-search-fields.ts | 58 + src/typings/methods-definition.ts | 6 + src/typings/model-options.ts | 9 + src/typings/modules.ts | 12 + src/typings/parse-schema.ts | 101 + src/typings/parsed-search-schema.ts | 8 + src/typings/point-units.ts | 1 + src/typings/point.ts | 1 + src/typings/return-doc.ts | 10 + src/typings/schema-definition.ts | 107 + src/typings/schema-options.ts | 7 + src/typings/search-information.ts | 6 + src/typings/url-object.ts | 6 + src/utils/index.ts | 2 + src/utils/parse.ts | 39 + src/utils/reference-array.ts | 15 + src/utils/symbols.ts | 2 + tsconfig.json | 41 +- vitest.config.js | 10 +- 229 files changed, 6317 insertions(+), 26096 deletions(-) create mode 100644 .gitattributes delete mode 100644 .nvmrc delete mode 100644 docs/.nojekyll delete mode 100644 docs/README.md delete mode 100644 docs/classes/AbstractSearch.md delete mode 100644 docs/classes/ArrayHashInput.md delete mode 100644 docs/classes/Circle.md delete mode 100644 docs/classes/Client.md delete mode 100644 docs/classes/Field.md delete mode 100644 docs/classes/FieldNotInSchema.md delete mode 100644 docs/classes/InvalidHashInput.md delete mode 100644 docs/classes/InvalidHashValue.md delete mode 100644 docs/classes/InvalidInput.md delete mode 100644 docs/classes/InvalidJsonInput.md delete mode 100644 docs/classes/InvalidJsonValue.md delete mode 100644 docs/classes/InvalidSchema.md delete mode 100644 docs/classes/InvalidValue.md delete mode 100644 docs/classes/NestedHashInput.md delete mode 100644 docs/classes/NullJsonInput.md delete mode 100644 docs/classes/NullJsonValue.md delete mode 100644 docs/classes/PointOutOfRange.md delete mode 100644 docs/classes/RawSearch.md delete mode 100644 docs/classes/RedisOmError.md delete mode 100644 docs/classes/Repository.md delete mode 100644 docs/classes/Schema.md delete mode 100644 docs/classes/Search.md delete mode 100644 docs/classes/SearchError.md delete mode 100644 docs/classes/SemanticSearchError.md delete mode 100644 docs/classes/Where.md delete mode 100644 docs/classes/WhereField.md create mode 100644 examples/table-module.ts delete mode 100644 lib/client/client.ts delete mode 100644 lib/client/index.ts delete mode 100644 lib/entity/entity.ts delete mode 100644 lib/entity/index.ts delete mode 100644 lib/error/index.ts delete mode 100644 lib/error/invalid-input.ts delete mode 100644 lib/error/invalid-schema.ts delete mode 100644 lib/error/invalid-value.ts delete mode 100644 lib/error/point-out-of-range.ts delete mode 100644 lib/error/redis-om-error.ts delete mode 100644 lib/error/search-error.ts delete mode 100644 lib/index.ts delete mode 100644 lib/indexer/index-builder.ts delete mode 100644 lib/indexer/index.ts delete mode 100644 lib/repository/index.ts delete mode 100644 lib/repository/repository.ts delete mode 100644 lib/schema/definitions.ts delete mode 100644 lib/schema/field.ts delete mode 100644 lib/schema/index.ts delete mode 100644 lib/schema/options.ts delete mode 100644 lib/schema/schema.ts delete mode 100644 lib/search/index.ts delete mode 100644 lib/search/results-converter.ts delete mode 100644 lib/search/search.ts delete mode 100644 lib/search/where-and.ts delete mode 100644 lib/search/where-boolean.ts delete mode 100644 lib/search/where-date.ts delete mode 100644 lib/search/where-field.ts delete mode 100644 lib/search/where-number.ts delete mode 100644 lib/search/where-or.ts delete mode 100644 lib/search/where-point.ts delete mode 100644 lib/search/where-string-array.ts delete mode 100644 lib/search/where-string.ts delete mode 100644 lib/search/where-text.ts delete mode 100644 lib/search/where.ts delete mode 100644 lib/transformer/from-hash-transformer.ts delete mode 100644 lib/transformer/from-json-transformer.ts delete mode 100644 lib/transformer/index.ts delete mode 100644 lib/transformer/to-hash-transformer.ts delete mode 100644 lib/transformer/to-json-transformer.ts delete mode 100644 lib/transformer/transformer-common.ts delete mode 100644 package-lock.json create mode 100644 pnpm-lock.yaml create mode 100644 spec/constants.ts delete mode 100644 spec/functional/core/fetch-hash.spec.ts delete mode 100644 spec/functional/core/fetch-json.spec.ts delete mode 100644 spec/functional/core/remove-hash.spec.ts delete mode 100644 spec/functional/core/remove-json.spec.ts delete mode 100644 spec/functional/core/save-hash.spec.ts delete mode 100644 spec/functional/core/save-json.spec.ts delete mode 100644 spec/functional/core/update-hash.spec.ts delete mode 100644 spec/functional/core/update-json.spec.ts delete mode 100644 spec/functional/demo.spec.ts delete mode 100644 spec/functional/helpers/data-helper.ts delete mode 100644 spec/functional/helpers/hash-example-data.ts delete mode 100644 spec/functional/helpers/json-example-data.ts delete mode 100644 spec/functional/helpers/redis-helper.ts delete mode 100644 spec/functional/search/create-and-drop-index-on-hash.spec.ts delete mode 100644 spec/functional/search/create-and-drop-index-on-json.spec.ts delete mode 100644 spec/functional/search/drop-missing-index.spec.ts delete mode 100644 spec/functional/search/search-hash.spec.ts delete mode 100644 spec/functional/search/search-json.spec.ts delete mode 100644 spec/helpers/custom-matchers.ts delete mode 100644 spec/helpers/example-data.ts create mode 100644 spec/schema/array.spec.ts create mode 100644 spec/schema/boolean.spec.ts create mode 100644 spec/schema/date.spec.ts create mode 100644 spec/schema/number.spec.ts create mode 100644 spec/schema/object.spec.ts create mode 100644 spec/schema/parser.spec.ts create mode 100644 spec/schema/point.spec.ts create mode 100644 spec/schema/string.spec.ts create mode 100644 spec/schema/text.spec.ts create mode 100644 spec/tsconfig.json delete mode 100644 spec/unit/client/client-close.spec.ts delete mode 100644 spec/unit/client/client-create-index.spec.ts delete mode 100644 spec/unit/client/client-drop-index.spec.ts delete mode 100644 spec/unit/client/client-expire.spec.ts delete mode 100644 spec/unit/client/client-fetch-repository.spec.ts delete mode 100644 spec/unit/client/client-get-set.spec.ts delete mode 100644 spec/unit/client/client-hgetall.spec.ts delete mode 100644 spec/unit/client/client-hsetall.spec.ts delete mode 100644 spec/unit/client/client-jsonget.spec.ts delete mode 100644 spec/unit/client/client-jsonset.spec.ts delete mode 100644 spec/unit/client/client-open.spec.ts delete mode 100644 spec/unit/client/client-search.spec.ts delete mode 100644 spec/unit/client/client-unlink.spec.ts delete mode 100644 spec/unit/client/client-use.spec.ts delete mode 100644 spec/unit/error/errors.spec.ts delete mode 100644 spec/unit/helpers/mock-client.ts delete mode 100644 spec/unit/helpers/mock-indexer.ts delete mode 100644 spec/unit/helpers/mock-redis.ts delete mode 100644 spec/unit/helpers/search-helpers.ts delete mode 100644 spec/unit/helpers/test-entity-and-schema.ts delete mode 100644 spec/unit/indexer/boolean-hash-fields.spec.ts delete mode 100644 spec/unit/indexer/boolean-json-fields.spec.ts delete mode 100644 spec/unit/indexer/date-hash-fields.spec.ts delete mode 100644 spec/unit/indexer/date-json-fields.spec.ts delete mode 100644 spec/unit/indexer/index-builder.spec.ts delete mode 100644 spec/unit/indexer/numeric-hash-fields.spec.ts delete mode 100644 spec/unit/indexer/numeric-json-fields.spec.ts delete mode 100644 spec/unit/indexer/point-hash-fields.spec.ts delete mode 100644 spec/unit/indexer/point-json-fields.spec.ts delete mode 100644 spec/unit/indexer/string-array-hash-fields.spec.ts delete mode 100644 spec/unit/indexer/string-array-json-fields.spec.ts delete mode 100644 spec/unit/indexer/string-hash-fields.spec.ts delete mode 100644 spec/unit/indexer/string-json-fields.spec.ts delete mode 100644 spec/unit/indexer/text-hash-fields.spec.ts delete mode 100644 spec/unit/indexer/text-json-fields.spec.ts delete mode 100644 spec/unit/repository/repository-constructor.spec.ts delete mode 100644 spec/unit/repository/repository-create-index.spec.ts delete mode 100644 spec/unit/repository/repository-drop-index.spec.ts delete mode 100644 spec/unit/repository/repository-expire.spec.ts delete mode 100644 spec/unit/repository/repository-fetch.spec.ts delete mode 100644 spec/unit/repository/repository-remove.spec.ts delete mode 100644 spec/unit/repository/repository-save.spec.ts delete mode 100644 spec/unit/repository/repository-search.spec.ts delete mode 100644 spec/unit/schema/field.spec.ts delete mode 100644 spec/unit/schema/schema-index-hash.spec.ts delete mode 100644 spec/unit/schema/schema.spec.ts delete mode 100644 spec/unit/search/raw-search-query.spec.ts delete mode 100644 spec/unit/search/search-by-boolean.spec.ts delete mode 100644 spec/unit/search/search-by-date.spec.ts delete mode 100644 spec/unit/search/search-by-number.spec.ts delete mode 100644 spec/unit/search/search-by-point.spec.ts delete mode 100644 spec/unit/search/search-by-string-array.spec.ts delete mode 100644 spec/unit/search/search-by-string.spec.ts delete mode 100644 spec/unit/search/search-by-text.spec.ts delete mode 100644 spec/unit/search/search-query.spec.ts delete mode 100644 spec/unit/search/search-return-all-ids.spec.ts delete mode 100644 spec/unit/search/search-return-all-keys.spec.ts delete mode 100644 spec/unit/search/search-return-all.spec.ts delete mode 100644 spec/unit/search/search-return-count.spec.ts delete mode 100644 spec/unit/search/search-return-first-id.spec.ts delete mode 100644 spec/unit/search/search-return-first-key.spec.ts delete mode 100644 spec/unit/search/search-return-first.spec.ts delete mode 100644 spec/unit/search/search-return-max-id.spec.ts delete mode 100644 spec/unit/search/search-return-max-key.spec.ts delete mode 100644 spec/unit/search/search-return-max.spec.ts delete mode 100644 spec/unit/search/search-return-min-id.spec.ts delete mode 100644 spec/unit/search/search-return-min-key.spec.ts delete mode 100644 spec/unit/search/search-return-min.spec.ts delete mode 100644 spec/unit/search/search-return-page-of-ids.spec.ts delete mode 100644 spec/unit/search/search-return-page-of-keys.spec.ts delete mode 100644 spec/unit/search/search-return-page.spec.ts delete mode 100644 spec/unit/search/search-sort-by.spec.ts delete mode 100644 spec/unit/transformer/from-redis-hash.spec.ts delete mode 100644 spec/unit/transformer/from-redis-json.spec.ts delete mode 100644 spec/unit/transformer/to-redis-hash.spec.ts delete mode 100644 spec/unit/transformer/to-redis-json.spec.ts create mode 100644 src/client.ts create mode 100644 src/document/document-helpers/general-helpers.ts create mode 100644 src/document/document-helpers/hash-helpers.ts create mode 100644 src/document/document-helpers/index.ts create mode 100644 src/document/document-helpers/json-helpers.ts create mode 100644 src/document/hash-document.ts create mode 100644 src/document/index.ts create mode 100644 src/document/json-document.ts create mode 100644 src/index.ts create mode 100644 src/model.ts create mode 100644 src/schema.ts create mode 100644 src/search/search-builders/base.ts create mode 100644 src/search/search-builders/boolean.ts create mode 100644 src/search/search-builders/date.ts create mode 100644 src/search/search-builders/index.ts create mode 100644 src/search/search-builders/number.ts create mode 100644 src/search/search-builders/point.ts create mode 100644 src/search/search-builders/string.ts create mode 100644 src/search/search-builders/text.ts create mode 100644 src/search/search-builders/vector.ts create mode 100644 src/search/search.ts create mode 100644 src/typings/client.ts create mode 100644 src/typings/doc-base.ts create mode 100644 src/typings/extract-generic.ts create mode 100644 src/typings/field-map.ts create mode 100644 src/typings/index.ts create mode 100644 src/typings/map-schema.ts create mode 100644 src/typings/map-search-fields.ts create mode 100644 src/typings/methods-definition.ts create mode 100644 src/typings/model-options.ts create mode 100644 src/typings/modules.ts create mode 100644 src/typings/parse-schema.ts create mode 100644 src/typings/parsed-search-schema.ts create mode 100644 src/typings/point-units.ts create mode 100644 src/typings/point.ts create mode 100644 src/typings/return-doc.ts create mode 100644 src/typings/schema-definition.ts create mode 100644 src/typings/schema-options.ts create mode 100644 src/typings/search-information.ts create mode 100644 src/typings/url-object.ts create mode 100644 src/utils/index.ts create mode 100644 src/utils/parse.ts create mode 100644 src/utils/reference-array.ts create mode 100644 src/utils/symbols.ts diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..e7c1d93 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text eol=lf diff --git a/.gitignore b/.gitignore index 8a771b4..9dd2987 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ -node_modules -dist -coverage -.DS_Store \ No newline at end of file +node_modules/ +dist/ +bench-dist/ +coverage/ +.DS_Store +.dccache \ No newline at end of file diff --git a/.nvmrc b/.nvmrc deleted file mode 100644 index 6f7f377..0000000 --- a/.nvmrc +++ /dev/null @@ -1 +0,0 @@ -v16 diff --git a/docs/.nojekyll b/docs/.nojekyll deleted file mode 100644 index e2ac661..0000000 --- a/docs/.nojekyll +++ /dev/null @@ -1 +0,0 @@ -TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false. \ No newline at end of file diff --git a/docs/README.md b/docs/README.md deleted file mode 100644 index 459f416..0000000 --- a/docs/README.md +++ /dev/null @@ -1,466 +0,0 @@ -redis-om - -# redis-om - -## Table of contents - -### Classes - -- [AbstractSearch](classes/AbstractSearch.md) -- [ArrayHashInput](classes/ArrayHashInput.md) -- [Circle](classes/Circle.md) -- [Client](classes/Client.md) -- [Field](classes/Field.md) -- [FieldNotInSchema](classes/FieldNotInSchema.md) -- [InvalidHashInput](classes/InvalidHashInput.md) -- [InvalidHashValue](classes/InvalidHashValue.md) -- [InvalidInput](classes/InvalidInput.md) -- [InvalidJsonInput](classes/InvalidJsonInput.md) -- [InvalidJsonValue](classes/InvalidJsonValue.md) -- [InvalidSchema](classes/InvalidSchema.md) -- [InvalidValue](classes/InvalidValue.md) -- [NestedHashInput](classes/NestedHashInput.md) -- [NullJsonInput](classes/NullJsonInput.md) -- [NullJsonValue](classes/NullJsonValue.md) -- [PointOutOfRange](classes/PointOutOfRange.md) -- [RawSearch](classes/RawSearch.md) -- [RedisOmError](classes/RedisOmError.md) -- [Repository](classes/Repository.md) -- [Schema](classes/Schema.md) -- [Search](classes/Search.md) -- [SearchError](classes/SearchError.md) -- [SemanticSearchError](classes/SemanticSearchError.md) -- [Where](classes/Where.md) -- [WhereField](classes/WhereField.md) - -### Type Aliases - -- [AllFieldDefinition](README.md#allfielddefinition) -- [BooleanFieldDefinition](README.md#booleanfielddefinition) -- [CircleFunction](README.md#circlefunction) -- [CommonFieldDefinition](README.md#commonfielddefinition) -- [DataStructure](README.md#datastructure) -- [DateFieldDefinition](README.md#datefielddefinition) -- [Entity](README.md#entity) -- [EntityData](README.md#entitydata) -- [EntityDataValue](README.md#entitydatavalue) -- [FieldDefinition](README.md#fielddefinition) -- [FieldType](README.md#fieldtype) -- [IdStrategy](README.md#idstrategy) -- [NumberFieldDefinition](README.md#numberfielddefinition) -- [Point](README.md#point) -- [PointFieldDefinition](README.md#pointfielddefinition) -- [RedisClientConnection](README.md#redisclientconnection) -- [RedisClusterConnection](README.md#redisclusterconnection) -- [RedisConnection](README.md#redisconnection) -- [SchemaDefinition](README.md#schemadefinition) -- [SchemaOptions](README.md#schemaoptions) -- [StopWordOptions](README.md#stopwordoptions) -- [StringArrayFieldDefinition](README.md#stringarrayfielddefinition) -- [StringFieldDefinition](README.md#stringfielddefinition) -- [SubSearchFunction](README.md#subsearchfunction) -- [TextFieldDefinition](README.md#textfielddefinition) - -### Variables - -- [EntityId](README.md#entityid) -- [EntityKeyName](README.md#entitykeyname) - -## Type Aliases - -### AllFieldDefinition - -Ƭ **AllFieldDefinition**: `Object` - -All configuration properties that any field might have, regardless of type. - -#### Type declaration - -| Name | Type | Description | -| :------ | :------ | :------ | -| `alias?` | `string` | The default field name in Redis is the property name defined in the [SchemaDefinition](README.md#schemadefinition). Overrides the field name for a Hash to this value or in the case of JSON documents, sets the JSONPath to this value preceded by `$.`. Overridden by [field](README.md#field) and/or [path](README.md#path) settings. **`Deprecated`** | -| `caseSensitive?` | `boolean` | Is the original case of this field indexed with Redis OM. Defaults to false. | -| `field?` | `string` | The field name used to store this in a Redis Hash. Defaults to the name used in the [SchemaDefinition](README.md#schemadefinition) or the [alias](README.md#alias) property. | -| `indexed?` | `boolean` | Is this field indexed and thus searchable with Redis OM. Defaults to true. | -| `matcher?` | ``"dm:en"`` \| ``"dm:fr"`` \| ``"dm:pt"`` \| ``"dm:es"`` | Enables setting the phonetic matcher to use, supported matchers are: dm:en - Double Metaphone for English dm:fr - Double Metaphone for French dm:pt - Double Metaphone for Portuguese dm:es - Double Metaphone for Spanish | -| `normalized?` | `boolean` | Is this (sortable) field normalized when indexed. Defaults to true. | -| `path?` | `string` | The JSONPath expression this field references. Used only by search and only for JSON documents. Defaults to the name used in the [SchemaDefinition](README.md#schemadefinition) or the [alias](README.md#alias) property prefixed with `$.` . | -| `separator?` | `string` | Due to how RediSearch works, strings and arrays are sometimes stored the same in Redis, as a simple string. This is the separator used to split those strings when it is an array. If your StringField contains this separator, this can cause problems. You can change it here to avoid those problems. Defaults to `\|`. | -| `sortable?` | `boolean` | Enables sorting by this field. | -| `stemming?` | `boolean` | Is word stemming applied to this field with Redis OM. Defaults to true. | -| `type` | [`FieldType`](README.md#fieldtype) | The type of the field (i.e. string, number, boolean, etc.) | -| `weight?` | `number` | Enables setting the weight to apply to a text field | - -#### Defined in - -[lib/schema/definitions.ts:5](https://github.com/redis/redis-om-node/blob/5777b6c/lib/schema/definitions.ts#L5) - -___ - -### BooleanFieldDefinition - -Ƭ **BooleanFieldDefinition**: { `type`: ``"boolean"`` } & [`CommonFieldDefinition`](README.md#commonfielddefinition) & `Pick`<[`AllFieldDefinition`](README.md#allfielddefinition), ``"sortable"``\> - -A field representing a boolean. - -#### Defined in - -[lib/schema/definitions.ts:78](https://github.com/redis/redis-om-node/blob/5777b6c/lib/schema/definitions.ts#L78) - -___ - -### CircleFunction - -Ƭ **CircleFunction**: (`circle`: [`Circle`](classes/Circle.md)) => [`Circle`](classes/Circle.md) - -#### Type declaration - -▸ (`circle`): [`Circle`](classes/Circle.md) - -A function that defines a circle for `.inCircle` searches. - -##### Parameters - -| Name | Type | -| :------ | :------ | -| `circle` | [`Circle`](classes/Circle.md) | - -##### Returns - -[`Circle`](classes/Circle.md) - -#### Defined in - -[lib/search/where-point.ts:8](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/where-point.ts#L8) - -___ - -### CommonFieldDefinition - -Ƭ **CommonFieldDefinition**: `Pick`<[`AllFieldDefinition`](README.md#allfielddefinition), ``"type"`` \| ``"alias"`` \| ``"indexed"`` \| ``"field"`` \| ``"path"``\> - -The configuration properties that all fields have in common. - -#### Defined in - -[lib/schema/definitions.ts:75](https://github.com/redis/redis-om-node/blob/5777b6c/lib/schema/definitions.ts#L75) - -___ - -### DataStructure - -Ƭ **DataStructure**: ``"HASH"`` \| ``"JSON"`` - -The type of data structure in Redis to map objects to. - -#### Defined in - -[lib/schema/options.ts:2](https://github.com/redis/redis-om-node/blob/5777b6c/lib/schema/options.ts#L2) - -___ - -### DateFieldDefinition - -Ƭ **DateFieldDefinition**: { `type`: ``"date"`` } & [`CommonFieldDefinition`](README.md#commonfielddefinition) & `Pick`<[`AllFieldDefinition`](README.md#allfielddefinition), ``"sortable"``\> - -A field representing a date/time. - -#### Defined in - -[lib/schema/definitions.ts:83](https://github.com/redis/redis-om-node/blob/5777b6c/lib/schema/definitions.ts#L83) - -___ - -### Entity - -Ƭ **Entity**: [`EntityData`](README.md#entitydata) & { `[EntityId]?`: `string` ; `[EntityKeyName]?`: `string` } - -Defines the objects returned from calls to [repositories](classes/Repository.md). - -#### Defined in - -[lib/entity/entity.ts:8](https://github.com/redis/redis-om-node/blob/5777b6c/lib/entity/entity.ts#L8) - -___ - -### EntityData - -Ƭ **EntityData**: `Object` - -The free-form data associated with an [Entity](README.md#entity). - -#### Index signature - -▪ [key: `string`]: [`EntityDataValue`](README.md#entitydatavalue) \| [`EntityData`](README.md#entitydata) \| ([`EntityDataValue`](README.md#entitydatavalue) \| [`EntityData`](README.md#entitydata))[] - -#### Defined in - -[lib/entity/entity.ts:18](https://github.com/redis/redis-om-node/blob/5777b6c/lib/entity/entity.ts#L18) - -___ - -### EntityDataValue - -Ƭ **EntityDataValue**: `string` \| `number` \| `boolean` \| `Date` \| [`Point`](README.md#point) \| ``null`` \| `undefined` - -Valid types for values in an [Entity](README.md#entity). - -#### Defined in - -[lib/entity/entity.ts:23](https://github.com/redis/redis-om-node/blob/5777b6c/lib/entity/entity.ts#L23) - -___ - -### FieldDefinition - -Ƭ **FieldDefinition**: [`BooleanFieldDefinition`](README.md#booleanfielddefinition) \| [`DateFieldDefinition`](README.md#datefielddefinition) \| [`NumberFieldDefinition`](README.md#numberfielddefinition) \| [`PointFieldDefinition`](README.md#pointfielddefinition) \| [`StringArrayFieldDefinition`](README.md#stringarrayfielddefinition) \| [`StringFieldDefinition`](README.md#stringfielddefinition) \| [`TextFieldDefinition`](README.md#textfielddefinition) - -Contains instructions telling how to map a property on an [Entity](README.md#entity) to Redis. - -#### Defined in - -[lib/schema/definitions.ts:112](https://github.com/redis/redis-om-node/blob/5777b6c/lib/schema/definitions.ts#L112) - -___ - -### FieldType - -Ƭ **FieldType**: ``"boolean"`` \| ``"date"`` \| ``"number"`` \| ``"point"`` \| ``"string"`` \| ``"string[]"`` \| ``"text"`` - -Valid field types for a [FieldDefinition](README.md#fielddefinition). - -#### Defined in - -[lib/schema/definitions.ts:2](https://github.com/redis/redis-om-node/blob/5777b6c/lib/schema/definitions.ts#L2) - -___ - -### IdStrategy - -Ƭ **IdStrategy**: () => `Promise`<`string`\> - -#### Type declaration - -▸ (): `Promise`<`string`\> - -A function that generates random entityIds. - -##### Returns - -`Promise`<`string`\> - -#### Defined in - -[lib/schema/options.ts:5](https://github.com/redis/redis-om-node/blob/5777b6c/lib/schema/options.ts#L5) - -___ - -### NumberFieldDefinition - -Ƭ **NumberFieldDefinition**: { `type`: ``"number"`` } & [`CommonFieldDefinition`](README.md#commonfielddefinition) & `Pick`<[`AllFieldDefinition`](README.md#allfielddefinition), ``"sortable"``\> - -A field representing a number. - -#### Defined in - -[lib/schema/definitions.ts:88](https://github.com/redis/redis-om-node/blob/5777b6c/lib/schema/definitions.ts#L88) - -___ - -### Point - -Ƭ **Point**: `Object` - -Defines a point on the globe using longitude and latitude. - -#### Type declaration - -| Name | Type | Description | -| :------ | :------ | :------ | -| `latitude` | `number` | The latitude of the point. | -| `longitude` | `number` | The longitude of the point. | - -#### Defined in - -[lib/entity/entity.ts:26](https://github.com/redis/redis-om-node/blob/5777b6c/lib/entity/entity.ts#L26) - -___ - -### PointFieldDefinition - -Ƭ **PointFieldDefinition**: { `type`: ``"point"`` } & [`CommonFieldDefinition`](README.md#commonfielddefinition) - -A field representing a point on the globe. - -#### Defined in - -[lib/schema/definitions.ts:93](https://github.com/redis/redis-om-node/blob/5777b6c/lib/schema/definitions.ts#L93) - -___ - -### RedisClientConnection - -Ƭ **RedisClientConnection**: `ReturnType` - -A conventional Redis connection. - -#### Defined in - -[lib/client/client.ts:8](https://github.com/redis/redis-om-node/blob/5777b6c/lib/client/client.ts#L8) - -___ - -### RedisClusterConnection - -Ƭ **RedisClusterConnection**: `ReturnType` - -A clustered Redis connection. - -#### Defined in - -[lib/client/client.ts:11](https://github.com/redis/redis-om-node/blob/5777b6c/lib/client/client.ts#L11) - -___ - -### RedisConnection - -Ƭ **RedisConnection**: [`RedisClientConnection`](README.md#redisclientconnection) \| [`RedisClusterConnection`](README.md#redisclusterconnection) - -A Redis connection, clustered or conventional. - -#### Defined in - -[lib/client/client.ts:14](https://github.com/redis/redis-om-node/blob/5777b6c/lib/client/client.ts#L14) - -___ - -### SchemaDefinition - -Ƭ **SchemaDefinition**: `Record`<`string`, [`FieldDefinition`](README.md#fielddefinition)\> - -Group of [FieldDefinition](README.md#fielddefinition)s that define the schema for an [Entity](README.md#entity). - -#### Defined in - -[lib/schema/definitions.ts:118](https://github.com/redis/redis-om-node/blob/5777b6c/lib/schema/definitions.ts#L118) - -___ - -### SchemaOptions - -Ƭ **SchemaOptions**: `Object` - -Configuration options for a [Schema](classes/Schema.md). - -#### Type declaration - -| Name | Type | Description | -| :------ | :------ | :------ | -| `dataStructure?` | [`DataStructure`](README.md#datastructure) | The data structure used to store the [Entity](README.md#entity) in Redis. Can be set to either `JSON` or `HASH`. Defaults to JSON. | -| `idStrategy?` | [`IdStrategy`](README.md#idstrategy) | A function that generates a random entityId. Defaults to a function that generates [ULIDs](https://github.com/ulid/spec). Combined with prefix to generate a Redis key. If prefix is `Foo` and idStratgey returns `12345` then the generated key would be `Foo:12345`. | -| `indexHashName?` | `string` | The name used by Redis OM to store the hash of the index for this [Schema](classes/Schema.md). Defaults to prefix followed by `:index:hash`. So, for a prefix of `Foo`, it would use `Foo:index:hash`. | -| `indexName?` | `string` | The name used by RediSearch to store the index for this [Schema](classes/Schema.md). Defaults to prefix followed by `:index`. So, for a prefix of `Foo`, it would use `Foo:index`. | -| `stopWords?` | `string`[] | Stop words to be used by this schema. If `useStopWords` is anything other than `CUSTOM`, this option is ignored. | -| `useStopWords?` | [`StopWordOptions`](README.md#stopwordoptions) | Configures the usage of stop words. Valid values are `OFF`, `DEFAULT`, and `CUSTOM`. Setting this to `OFF` disables all stop words. Setting this to `DEFAULT` uses the stop words intrinsic to RediSearch. Setting this to `CUSTOM` tells RediSearch to use the stop words in `stopWords`. | - -#### Defined in - -[lib/schema/options.ts:11](https://github.com/redis/redis-om-node/blob/5777b6c/lib/schema/options.ts#L11) - -___ - -### StopWordOptions - -Ƭ **StopWordOptions**: ``"OFF"`` \| ``"DEFAULT"`` \| ``"CUSTOM"`` - -Valid values for how to use stop words for a given [Schema](classes/Schema.md). - -#### Defined in - -[lib/schema/options.ts:8](https://github.com/redis/redis-om-node/blob/5777b6c/lib/schema/options.ts#L8) - -___ - -### StringArrayFieldDefinition - -Ƭ **StringArrayFieldDefinition**: { `type`: ``"string[]"`` } & [`CommonFieldDefinition`](README.md#commonfielddefinition) & `Pick`<[`AllFieldDefinition`](README.md#allfielddefinition), ``"sortable"`` \| ``"caseSensitive"`` \| ``"normalized"`` \| ``"separator"``\> - -A field representing an array of strings. - -#### Defined in - -[lib/schema/definitions.ts:97](https://github.com/redis/redis-om-node/blob/5777b6c/lib/schema/definitions.ts#L97) - -___ - -### StringFieldDefinition - -Ƭ **StringFieldDefinition**: { `type`: ``"string"`` } & [`CommonFieldDefinition`](README.md#commonfielddefinition) & `Pick`<[`AllFieldDefinition`](README.md#allfielddefinition), ``"sortable"`` \| ``"caseSensitive"`` \| ``"normalized"`` \| ``"separator"``\> - -A field representing a whole string. - -#### Defined in - -[lib/schema/definitions.ts:102](https://github.com/redis/redis-om-node/blob/5777b6c/lib/schema/definitions.ts#L102) - -___ - -### SubSearchFunction - -Ƭ **SubSearchFunction**: (`search`: [`Search`](classes/Search.md)) => [`Search`](classes/Search.md) - -#### Type declaration - -▸ (`search`): [`Search`](classes/Search.md) - -A function that takes a [Search](classes/Search.md) and returns a [Search](classes/Search.md). Used in nested queries. - -##### Parameters - -| Name | Type | -| :------ | :------ | -| `search` | [`Search`](classes/Search.md) | - -##### Returns - -[`Search`](classes/Search.md) - -#### Defined in - -[lib/search/search.ts:26](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L26) - -___ - -### TextFieldDefinition - -Ƭ **TextFieldDefinition**: { `type`: ``"text"`` } & [`CommonFieldDefinition`](README.md#commonfielddefinition) & `Pick`<[`AllFieldDefinition`](README.md#allfielddefinition), ``"sortable"`` \| ``"normalized"`` \| ``"matcher"`` \| ``"stemming"`` \| ``"weight"``\> - -A field representing searchable text. - -#### Defined in - -[lib/schema/definitions.ts:107](https://github.com/redis/redis-om-node/blob/5777b6c/lib/schema/definitions.ts#L107) - -## Variables - -### EntityId - -• `Const` **EntityId**: typeof [`EntityId`](README.md#entityid) - -The Symbol used to access the entity ID of an [Entity](README.md#entity). - -#### Defined in - -[lib/entity/entity.ts:2](https://github.com/redis/redis-om-node/blob/5777b6c/lib/entity/entity.ts#L2) - -___ - -### EntityKeyName - -• `Const` **EntityKeyName**: typeof [`EntityKeyName`](README.md#entitykeyname) - -The Symbol used to access the keyname of an [Entity](README.md#entity). - -#### Defined in - -[lib/entity/entity.ts:5](https://github.com/redis/redis-om-node/blob/5777b6c/lib/entity/entity.ts#L5) diff --git a/docs/classes/AbstractSearch.md b/docs/classes/AbstractSearch.md deleted file mode 100644 index fb3309f..0000000 --- a/docs/classes/AbstractSearch.md +++ /dev/null @@ -1,912 +0,0 @@ -[redis-om](../README.md) / AbstractSearch - -# Class: AbstractSearch - -Abstract base class for [Search](Search.md) and [RawSearch](RawSearch.md) that -contains methods to return search results. - -**`Template`** - -The type of [Entity](../README.md#entity) being sought. - -## Hierarchy - -- **`AbstractSearch`** - - ↳ [`RawSearch`](RawSearch.md) - - ↳ [`Search`](Search.md) - -## Table of contents - -### Accessors - -- [return](AbstractSearch.md#return) - -### Methods - -- [all](AbstractSearch.md#all) -- [allIds](AbstractSearch.md#allids) -- [allKeys](AbstractSearch.md#allkeys) -- [count](AbstractSearch.md#count) -- [first](AbstractSearch.md#first) -- [firstId](AbstractSearch.md#firstid) -- [firstKey](AbstractSearch.md#firstkey) -- [max](AbstractSearch.md#max) -- [maxId](AbstractSearch.md#maxid) -- [maxKey](AbstractSearch.md#maxkey) -- [min](AbstractSearch.md#min) -- [minId](AbstractSearch.md#minid) -- [minKey](AbstractSearch.md#minkey) -- [page](AbstractSearch.md#page) -- [pageOfIds](AbstractSearch.md#pageofids) -- [pageOfKeys](AbstractSearch.md#pageofkeys) -- [returnAll](AbstractSearch.md#returnall) -- [returnAllIds](AbstractSearch.md#returnallids) -- [returnAllKeys](AbstractSearch.md#returnallkeys) -- [returnCount](AbstractSearch.md#returncount) -- [returnFirst](AbstractSearch.md#returnfirst) -- [returnFirstId](AbstractSearch.md#returnfirstid) -- [returnFirstKey](AbstractSearch.md#returnfirstkey) -- [returnMax](AbstractSearch.md#returnmax) -- [returnMaxId](AbstractSearch.md#returnmaxid) -- [returnMaxKey](AbstractSearch.md#returnmaxkey) -- [returnMin](AbstractSearch.md#returnmin) -- [returnMinId](AbstractSearch.md#returnminid) -- [returnMinKey](AbstractSearch.md#returnminkey) -- [returnPage](AbstractSearch.md#returnpage) -- [returnPageOfIds](AbstractSearch.md#returnpageofids) -- [returnPageOfKeys](AbstractSearch.md#returnpageofkeys) -- [sortAsc](AbstractSearch.md#sortasc) -- [sortAscending](AbstractSearch.md#sortascending) -- [sortBy](AbstractSearch.md#sortby) -- [sortDesc](AbstractSearch.md#sortdesc) -- [sortDescending](AbstractSearch.md#sortdescending) - -## Accessors - -### return - -• `get` **return**(): [`AbstractSearch`](AbstractSearch.md) - -Returns the current instance. Syntactic sugar to make your code more fluent. - -#### Returns - -[`AbstractSearch`](AbstractSearch.md) - -this - -#### Defined in - -[lib/search/search.ts:308](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L308) - -## Methods - -### all - -▸ **all**(`options?`): `Promise`<[`Entity`](../README.md#entity)[]\> - -Returns all the [Entities](../README.md#entity) that match this query. This method -makes multiple calls to Redis until all the [Entities](../README.md#entity) are returned. -You can specify the batch size by setting the `pageSize` property on the -options: - -```typescript -const entities = await repository.search().returnAll({ pageSize: 100 }) -``` - -#### Parameters - -| Name | Type | Default value | Description | -| :------ | :------ | :------ | :------ | -| `options` | `Object` | `undefined` | Options for the call. | -| `options.pageSize` | `number` | `10` | Number of [Entities](../README.md#entity) returned per batch. | - -#### Returns - -`Promise`<[`Entity`](../README.md#entity)[]\> - -An array of [Entities](../README.md#entity) matching the query. - -#### Defined in - -[lib/search/search.ts:264](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L264) - -___ - -### allIds - -▸ **allIds**(`options?`): `Promise`<`string`[]\> - -Returns all the entity IDs that match this query. This method -makes multiple calls to Redis until all the entity IDs are returned. -You can specify the batch size by setting the `pageSize` property on the -options: - -```typescript -const keys = await repository.search().returnAllIds({ pageSize: 100 }) -``` - -#### Parameters - -| Name | Type | Default value | Description | -| :------ | :------ | :------ | :------ | -| `options` | `Object` | `undefined` | Options for the call. | -| `options.pageSize` | `number` | `10` | Number of entity IDs returned per batch. | - -#### Returns - -`Promise`<`string`[]\> - -An array of entity IDs matching the query. - -#### Defined in - -[lib/search/search.ts:282](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L282) - -___ - -### allKeys - -▸ **allKeys**(`options?`): `Promise`<`string`[]\> - -Returns all the key names in Redis that match this query. This method -makes multiple calls to Redis until all the key names are returned. -You can specify the batch size by setting the `pageSize` property on the -options: - -```typescript -const keys = await repository.search().returnAllKeys({ pageSize: 100 }) -``` - -#### Parameters - -| Name | Type | Default value | Description | -| :------ | :------ | :------ | :------ | -| `options` | `Object` | `undefined` | Options for the call. | -| `options.pageSize` | `number` | `10` | Number of key names returned per batch. | - -#### Returns - -`Promise`<`string`[]\> - -An array of key names matching the query. - -#### Defined in - -[lib/search/search.ts:300](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L300) - -___ - -### count - -▸ **count**(): `Promise`<`number`\> - -Returns the number of [Entities](../README.md#entity) that match this query. - -#### Returns - -`Promise`<`number`\> - -#### Defined in - -[lib/search/search.ts:188](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L188) - -___ - -### first - -▸ **first**(): `Promise`<``null`` \| [`Entity`](../README.md#entity)\> - -Returns the first [Entity](../README.md#entity) that matches this query. - -#### Returns - -`Promise`<``null`` \| [`Entity`](../README.md#entity)\> - -#### Defined in - -[lib/search/search.ts:229](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L229) - -___ - -### firstId - -▸ **firstId**(): `Promise`<``null`` \| `string`\> - -Returns the first entity ID that matches this query. - -#### Returns - -`Promise`<``null`` \| `string`\> - -#### Defined in - -[lib/search/search.ts:237](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L237) - -___ - -### firstKey - -▸ **firstKey**(): `Promise`<``null`` \| `string`\> - -Returns the first key name that matches this query. - -#### Returns - -`Promise`<``null`` \| `string`\> - -#### Defined in - -[lib/search/search.ts:245](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L245) - -___ - -### max - -▸ **max**(`field`): `Promise`<``null`` \| [`Entity`](../README.md#entity)\> - -Finds the [Entity](../README.md#entity) with the maximal value for a field. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `field` | `string` | The field with the maximal value. | - -#### Returns - -`Promise`<``null`` \| [`Entity`](../README.md#entity)\> - -The entity ID [Entity](../README.md#entity) with the maximal value - -#### Defined in - -[lib/search/search.ts:162](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L162) - -___ - -### maxId - -▸ **maxId**(`field`): `Promise`<``null`` \| `string`\> - -Finds the entity ID with the maximal value for a field. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `field` | `string` | The field with the maximal value. | - -#### Returns - -`Promise`<``null`` \| `string`\> - -The entity ID with the maximal value - -#### Defined in - -[lib/search/search.ts:171](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L171) - -___ - -### maxKey - -▸ **maxKey**(`field`): `Promise`<``null`` \| `string`\> - -Finds the key name in Redis with the maximal value for a field. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `field` | `string` | The field with the maximal value. | - -#### Returns - -`Promise`<``null`` \| `string`\> - -The key name with the maximal value - -#### Defined in - -[lib/search/search.ts:180](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L180) - -___ - -### min - -▸ **min**(`field`): `Promise`<``null`` \| [`Entity`](../README.md#entity)\> - -Finds the [Entity](../README.md#entity) with the minimal value for a field. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `field` | `string` | The field with the minimal value. | - -#### Returns - -`Promise`<``null`` \| [`Entity`](../README.md#entity)\> - -The [Entity](../README.md#entity) with the minimal value - -#### Defined in - -[lib/search/search.ts:135](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L135) - -___ - -### minId - -▸ **minId**(`field`): `Promise`<``null`` \| `string`\> - -Finds the entity ID with the minimal value for a field. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `field` | `string` | The field with the minimal value. | - -#### Returns - -`Promise`<``null`` \| `string`\> - -The entity ID with the minimal value - -#### Defined in - -[lib/search/search.ts:144](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L144) - -___ - -### minKey - -▸ **minKey**(`field`): `Promise`<``null`` \| `string`\> - -Finds the key name in Redis with the minimal value for a field. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `field` | `string` | The field with the minimal value. | - -#### Returns - -`Promise`<``null`` \| `string`\> - -The key name with the minimal value - -#### Defined in - -[lib/search/search.ts:153](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L153) - -___ - -### page - -▸ **page**(`offset`, `count`): `Promise`<[`Entity`](../README.md#entity)[]\> - -Returns a page of [Entities](../README.md#entity) that match this query. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `offset` | `number` | The offset for where to start returning [Entities](../README.md#entity). | -| `count` | `number` | The number of [Entities](../README.md#entity) to return. | - -#### Returns - -`Promise`<[`Entity`](../README.md#entity)[]\> - -An array of [Entities](../README.md#entity) matching the query. - -#### Defined in - -[lib/search/search.ts:199](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L199) - -___ - -### pageOfIds - -▸ **pageOfIds**(`offset`, `count`): `Promise`<`string`[]\> - -Returns a page of entity IDs that match this query. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `offset` | `number` | The offset for where to start returning entity IDs. | -| `count` | `number` | The number of entity IDs to return. | - -#### Returns - -`Promise`<`string`[]\> - -An array of strings matching the query. - -#### Defined in - -[lib/search/search.ts:210](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L210) - -___ - -### pageOfKeys - -▸ **pageOfKeys**(`offset`, `count`): `Promise`<`string`[]\> - -Returns a page of key names in Redis that match this query. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `offset` | `number` | The offset for where to start returning key names. | -| `count` | `number` | The number of key names to return. | - -#### Returns - -`Promise`<`string`[]\> - -An array of strings matching the query. - -#### Defined in - -[lib/search/search.ts:221](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L221) - -___ - -### returnAll - -▸ **returnAll**(`options?`): `Promise`<[`Entity`](../README.md#entity)[]\> - -Alias for [all](Search.md#all). - -#### Parameters - -| Name | Type | Default value | -| :------ | :------ | :------ | -| `options` | `Object` | `undefined` | -| `options.pageSize` | `number` | `10` | - -#### Returns - -`Promise`<[`Entity`](../README.md#entity)[]\> - -#### Defined in - -[lib/search/search.ts:406](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L406) - -___ - -### returnAllIds - -▸ **returnAllIds**(`options?`): `Promise`<`string`[]\> - -Alias for [allIds](Search.md#allids). - -#### Parameters - -| Name | Type | Default value | -| :------ | :------ | :------ | -| `options` | `Object` | `undefined` | -| `options.pageSize` | `number` | `10` | - -#### Returns - -`Promise`<`string`[]\> - -#### Defined in - -[lib/search/search.ts:413](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L413) - -___ - -### returnAllKeys - -▸ **returnAllKeys**(`options?`): `Promise`<`string`[]\> - -Alias for [allKeys](Search.md#allkeys). - -#### Parameters - -| Name | Type | Default value | -| :------ | :------ | :------ | -| `options` | `Object` | `undefined` | -| `options.pageSize` | `number` | `10` | - -#### Returns - -`Promise`<`string`[]\> - -#### Defined in - -[lib/search/search.ts:420](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L420) - -___ - -### returnCount - -▸ **returnCount**(): `Promise`<`number`\> - -Alias for [count](Search.md#count). - -#### Returns - -`Promise`<`number`\> - -#### Defined in - -[lib/search/search.ts:357](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L357) - -___ - -### returnFirst - -▸ **returnFirst**(): `Promise`<``null`` \| [`Entity`](../README.md#entity)\> - -Alias for [first](Search.md#first). - -#### Returns - -`Promise`<``null`` \| [`Entity`](../README.md#entity)\> - -#### Defined in - -[lib/search/search.ts:385](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L385) - -___ - -### returnFirstId - -▸ **returnFirstId**(): `Promise`<``null`` \| `string`\> - -Alias for [firstId](Search.md#firstid). - -#### Returns - -`Promise`<``null`` \| `string`\> - -#### Defined in - -[lib/search/search.ts:392](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L392) - -___ - -### returnFirstKey - -▸ **returnFirstKey**(): `Promise`<``null`` \| `string`\> - -Alias for [firstKey](Search.md#firstkey). - -#### Returns - -`Promise`<``null`` \| `string`\> - -#### Defined in - -[lib/search/search.ts:399](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L399) - -___ - -### returnMax - -▸ **returnMax**(`field`): `Promise`<``null`` \| [`Entity`](../README.md#entity)\> - -Alias for [max](Search.md#max). - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `field` | `string` | - -#### Returns - -`Promise`<``null`` \| [`Entity`](../README.md#entity)\> - -#### Defined in - -[lib/search/search.ts:336](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L336) - -___ - -### returnMaxId - -▸ **returnMaxId**(`field`): `Promise`<``null`` \| `string`\> - -Alias for [maxId](Search.md#maxid). - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `field` | `string` | - -#### Returns - -`Promise`<``null`` \| `string`\> - -#### Defined in - -[lib/search/search.ts:343](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L343) - -___ - -### returnMaxKey - -▸ **returnMaxKey**(`field`): `Promise`<``null`` \| `string`\> - -Alias for [maxKey](Search.md#maxkey). - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `field` | `string` | - -#### Returns - -`Promise`<``null`` \| `string`\> - -#### Defined in - -[lib/search/search.ts:350](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L350) - -___ - -### returnMin - -▸ **returnMin**(`field`): `Promise`<``null`` \| [`Entity`](../README.md#entity)\> - -Alias for [min](Search.md#min). - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `field` | `string` | - -#### Returns - -`Promise`<``null`` \| [`Entity`](../README.md#entity)\> - -#### Defined in - -[lib/search/search.ts:315](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L315) - -___ - -### returnMinId - -▸ **returnMinId**(`field`): `Promise`<``null`` \| `string`\> - -Alias for [minId](Search.md#minid). - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `field` | `string` | - -#### Returns - -`Promise`<``null`` \| `string`\> - -#### Defined in - -[lib/search/search.ts:322](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L322) - -___ - -### returnMinKey - -▸ **returnMinKey**(`field`): `Promise`<``null`` \| `string`\> - -Alias for [minKey](Search.md#minkey). - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `field` | `string` | - -#### Returns - -`Promise`<``null`` \| `string`\> - -#### Defined in - -[lib/search/search.ts:329](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L329) - -___ - -### returnPage - -▸ **returnPage**(`offset`, `count`): `Promise`<[`Entity`](../README.md#entity)[]\> - -Alias for [page](Search.md#page). - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `offset` | `number` | -| `count` | `number` | - -#### Returns - -`Promise`<[`Entity`](../README.md#entity)[]\> - -#### Defined in - -[lib/search/search.ts:364](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L364) - -___ - -### returnPageOfIds - -▸ **returnPageOfIds**(`offset`, `count`): `Promise`<`string`[]\> - -Alias for [pageOfIds](Search.md#pageofids). - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `offset` | `number` | -| `count` | `number` | - -#### Returns - -`Promise`<`string`[]\> - -#### Defined in - -[lib/search/search.ts:371](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L371) - -___ - -### returnPageOfKeys - -▸ **returnPageOfKeys**(`offset`, `count`): `Promise`<`string`[]\> - -Alias for [pageOfKeys](Search.md#pageofkeys). - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `offset` | `number` | -| `count` | `number` | - -#### Returns - -`Promise`<`string`[]\> - -#### Defined in - -[lib/search/search.ts:378](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L378) - -___ - -### sortAsc - -▸ **sortAsc**(`field`): [`AbstractSearch`](AbstractSearch.md) - -Alias for [sortAscending](Search.md#sortascending). - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `field` | `string` | - -#### Returns - -[`AbstractSearch`](AbstractSearch.md) - -#### Defined in - -[lib/search/search.ts:86](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L86) - -___ - -### sortAscending - -▸ **sortAscending**(`field`): [`AbstractSearch`](AbstractSearch.md) - -Applies an ascending sort to the query. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `field` | `string` | The field to sort by. | - -#### Returns - -[`AbstractSearch`](AbstractSearch.md) - -this - -#### Defined in - -[lib/search/search.ts:63](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L63) - -___ - -### sortBy - -▸ **sortBy**(`fieldName`, `order?`): [`AbstractSearch`](AbstractSearch.md) - -Applies sorting for the query. - -#### Parameters - -| Name | Type | Default value | Description | -| :------ | :------ | :------ | :------ | -| `fieldName` | `string` | `undefined` | - | -| `order` | ``"ASC"`` \| ``"DESC"`` | `'ASC'` | The order of returned [Entities](../README.md#entity) Defaults to `ASC` (ascending) if not specified | - -#### Returns - -[`AbstractSearch`](AbstractSearch.md) - -this - -#### Defined in - -[lib/search/search.ts:96](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L96) - -___ - -### sortDesc - -▸ **sortDesc**(`field`): [`AbstractSearch`](AbstractSearch.md) - -Alias for [sortDescending](Search.md#sortdescending). - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `field` | `string` | - -#### Returns - -[`AbstractSearch`](AbstractSearch.md) - -#### Defined in - -[lib/search/search.ts:70](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L70) - -___ - -### sortDescending - -▸ **sortDescending**(`field`): [`AbstractSearch`](AbstractSearch.md) - -Applies a descending sort to the query. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `field` | `string` | The field to sort by. | - -#### Returns - -[`AbstractSearch`](AbstractSearch.md) - -this - -#### Defined in - -[lib/search/search.ts:79](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L79) diff --git a/docs/classes/ArrayHashInput.md b/docs/classes/ArrayHashInput.md deleted file mode 100644 index ddf3057..0000000 --- a/docs/classes/ArrayHashInput.md +++ /dev/null @@ -1,198 +0,0 @@ -[redis-om](../README.md) / ArrayHashInput - -# Class: ArrayHashInput - -## Hierarchy - -- [`InvalidInput`](InvalidInput.md) - - ↳ **`ArrayHashInput`** - -## Table of contents - -### Constructors - -- [constructor](ArrayHashInput.md#constructor) - -### Properties - -- [cause](ArrayHashInput.md#cause) -- [message](ArrayHashInput.md#message) -- [name](ArrayHashInput.md#name) -- [stack](ArrayHashInput.md#stack) -- [prepareStackTrace](ArrayHashInput.md#preparestacktrace) -- [stackTraceLimit](ArrayHashInput.md#stacktracelimit) - -### Accessors - -- [field](ArrayHashInput.md#field) - -### Methods - -- [captureStackTrace](ArrayHashInput.md#capturestacktrace) - -## Constructors - -### constructor - -• **new ArrayHashInput**(`property`) - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `property` | `string` | - -#### Overrides - -[InvalidInput](InvalidInput.md).[constructor](InvalidInput.md#constructor) - -#### Defined in - -[lib/error/invalid-input.ts:67](https://github.com/redis/redis-om-node/blob/5777b6c/lib/error/invalid-input.ts#L67) - -## Properties - -### cause - -• `Optional` **cause**: `unknown` - -#### Inherited from - -[InvalidInput](InvalidInput.md).[cause](InvalidInput.md#cause) - -#### Defined in - -node_modules/typescript/lib/lib.es2022.error.d.ts:26 - -___ - -### message - -• **message**: `string` - -#### Inherited from - -[InvalidInput](InvalidInput.md).[message](InvalidInput.md#message) - -#### Defined in - -node_modules/typescript/lib/lib.es5.d.ts:1054 - -___ - -### name - -• **name**: `string` - -#### Inherited from - -[InvalidInput](InvalidInput.md).[name](InvalidInput.md#name) - -#### Defined in - -node_modules/typescript/lib/lib.es5.d.ts:1053 - -___ - -### stack - -• `Optional` **stack**: `string` - -#### Inherited from - -[InvalidInput](InvalidInput.md).[stack](InvalidInput.md#stack) - -#### Defined in - -node_modules/typescript/lib/lib.es5.d.ts:1055 - -___ - -### prepareStackTrace - -▪ `Static` `Optional` **prepareStackTrace**: (`err`: `Error`, `stackTraces`: `CallSite`[]) => `any` - -#### Type declaration - -▸ (`err`, `stackTraces`): `any` - -Optional override for formatting stack traces - -**`See`** - -https://v8.dev/docs/stack-trace-api#customizing-stack-traces - -##### Parameters - -| Name | Type | -| :------ | :------ | -| `err` | `Error` | -| `stackTraces` | `CallSite`[] | - -##### Returns - -`any` - -#### Inherited from - -[InvalidInput](InvalidInput.md).[prepareStackTrace](InvalidInput.md#preparestacktrace) - -#### Defined in - -node_modules/@types/node/globals.d.ts:11 - -___ - -### stackTraceLimit - -▪ `Static` **stackTraceLimit**: `number` - -#### Inherited from - -[InvalidInput](InvalidInput.md).[stackTraceLimit](InvalidInput.md#stacktracelimit) - -#### Defined in - -node_modules/@types/node/globals.d.ts:13 - -## Accessors - -### field - -• `get` **field**(): `string` - -#### Returns - -`string` - -#### Defined in - -[lib/error/invalid-input.ts:73](https://github.com/redis/redis-om-node/blob/5777b6c/lib/error/invalid-input.ts#L73) - -## Methods - -### captureStackTrace - -▸ `Static` **captureStackTrace**(`targetObject`, `constructorOpt?`): `void` - -Create .stack property on a target object - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `targetObject` | `object` | -| `constructorOpt?` | `Function` | - -#### Returns - -`void` - -#### Inherited from - -[InvalidInput](InvalidInput.md).[captureStackTrace](InvalidInput.md#capturestacktrace) - -#### Defined in - -node_modules/@types/node/globals.d.ts:4 diff --git a/docs/classes/Circle.md b/docs/classes/Circle.md deleted file mode 100644 index cfb265d..0000000 --- a/docs/classes/Circle.md +++ /dev/null @@ -1,375 +0,0 @@ -[redis-om](../README.md) / Circle - -# Class: Circle - -A builder that defines a circle. - -## Table of contents - -### Constructors - -- [constructor](Circle.md#constructor) - -### Accessors - -- [feet](Circle.md#feet) -- [foot](Circle.md#foot) -- [ft](Circle.md#ft) -- [kilometer](Circle.md#kilometer) -- [kilometers](Circle.md#kilometers) -- [km](Circle.md#km) -- [m](Circle.md#m) -- [meter](Circle.md#meter) -- [meters](Circle.md#meters) -- [mi](Circle.md#mi) -- [mile](Circle.md#mile) -- [miles](Circle.md#miles) - -### Methods - -- [latitude](Circle.md#latitude) -- [longitude](Circle.md#longitude) -- [origin](Circle.md#origin) -- [radius](Circle.md#radius) - -## Constructors - -### constructor - -• **new Circle**() - -## Accessors - -### feet - -• `get` **feet**(): `this` - -Sets the units to feet. - -#### Returns - -`this` - -This instance. - -#### Defined in - -[lib/search/where-point.ts:149](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/where-point.ts#L149) - -___ - -### foot - -• `get` **foot**(): `this` - -Sets the units to feet. - -#### Returns - -`this` - -This instance. - -#### Defined in - -[lib/search/where-point.ts:143](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/where-point.ts#L143) - -___ - -### ft - -• `get` **ft**(): `this` - -Sets the units to feet. - -#### Returns - -`this` - -This instance. - -#### Defined in - -[lib/search/where-point.ts:137](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/where-point.ts#L137) - -___ - -### kilometer - -• `get` **kilometer**(): `this` - -Sets the units to kilometers. - -#### Returns - -`this` - -This instance. - -#### Defined in - -[lib/search/where-point.ts:122](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/where-point.ts#L122) - -___ - -### kilometers - -• `get` **kilometers**(): `this` - -Sets the units to kilometers. - -#### Returns - -`this` - -This instance. - -#### Defined in - -[lib/search/where-point.ts:128](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/where-point.ts#L128) - -___ - -### km - -• `get` **km**(): `this` - -Sets the units to kilometers. - -#### Returns - -`this` - -This instance. - -#### Defined in - -[lib/search/where-point.ts:116](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/where-point.ts#L116) - -___ - -### m - -• `get` **m**(): `this` - -Sets the units to meters. - -#### Returns - -`this` - -This instance. - -#### Defined in - -[lib/search/where-point.ts:95](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/where-point.ts#L95) - -___ - -### meter - -• `get` **meter**(): `this` - -Sets the units to meters. - -#### Returns - -`this` - -This instance. - -#### Defined in - -[lib/search/where-point.ts:101](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/where-point.ts#L101) - -___ - -### meters - -• `get` **meters**(): `this` - -Sets the units to meters. - -#### Returns - -`this` - -This instance. - -#### Defined in - -[lib/search/where-point.ts:107](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/where-point.ts#L107) - -___ - -### mi - -• `get` **mi**(): `this` - -Sets the units to miles. - -#### Returns - -`this` - -This instance. - -#### Defined in - -[lib/search/where-point.ts:158](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/where-point.ts#L158) - -___ - -### mile - -• `get` **mile**(): `this` - -Sets the units to miles. - -#### Returns - -`this` - -This instance. - -#### Defined in - -[lib/search/where-point.ts:164](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/where-point.ts#L164) - -___ - -### miles - -• `get` **miles**(): `this` - -Sets the units to miles. - -#### Returns - -`this` - -This instance. - -#### Defined in - -[lib/search/where-point.ts:170](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/where-point.ts#L170) - -## Methods - -### latitude - -▸ **latitude**(`value`): [`Circle`](Circle.md) - -Sets the latitude. If not set, defaults to 0.0. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `value` | `number` | The latitude. | - -#### Returns - -[`Circle`](Circle.md) - -This instance. - -#### Defined in - -[lib/search/where-point.ts:42](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/where-point.ts#L42) - -___ - -### longitude - -▸ **longitude**(`value`): [`Circle`](Circle.md) - -Sets the longitude. If not set, defaults to 0.0. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `value` | `number` | The longitude. | - -#### Returns - -[`Circle`](Circle.md) - -This instance. - -#### Defined in - -[lib/search/where-point.ts:31](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/where-point.ts#L31) - -___ - -### origin - -▸ **origin**(`point`): [`Circle`](Circle.md) - -Sets the origin of the circle using a [Point](../README.md#point). If not -set, defaults to [Null Island](https://en.wikipedia.org/wiki/Null_Island). - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `point` | [`Point`](../README.md#point) | A [Point](../README.md#point) containing the longitude and latitude of the origin. | - -#### Returns - -[`Circle`](Circle.md) - -This instance. - -#### Defined in - -[lib/search/where-point.ts:54](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/where-point.ts#L54) - -▸ **origin**(`longitude`, `latitude`): [`Circle`](Circle.md) - -Sets the origin of the circle. If not set, defaults to -[Null Island](https://en.wikipedia.org/wiki/Null_Island). - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `longitude` | `number` | The longitude. | -| `latitude` | `number` | The latitude. | - -#### Returns - -[`Circle`](Circle.md) - -This instance. - -#### Defined in - -[lib/search/where-point.ts:64](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/where-point.ts#L64) - -___ - -### radius - -▸ **radius**(`size`): [`Circle`](Circle.md) - -Sets the radius of the [Circle](Circle.md). Defaults to 1. If units are -not specified, defaults to meters. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `size` | `number` | The radius of the circle. | - -#### Returns - -[`Circle`](Circle.md) - -This instance. - -#### Defined in - -[lib/search/where-point.ts:86](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/where-point.ts#L86) diff --git a/docs/classes/Client.md b/docs/classes/Client.md deleted file mode 100644 index 20b5995..0000000 --- a/docs/classes/Client.md +++ /dev/null @@ -1,190 +0,0 @@ -[redis-om](../README.md) / Client - -# Class: Client - -A Client is the starting point for working with Redis OM. Clients manage the -connection to Redis and provide limited functionality for executing Redis commands. -Create a client and open it before you use it: - -```typescript -const client = new Client() -await client.open() -``` - -A Client is primarily used by a [Repository](Repository.md) which requires a client in -its constructor. - -**`Deprecated`** - -Just used Node Redis client directly and pass it to the Repository. - -## Table of contents - -### Constructors - -- [constructor](Client.md#constructor) - -### Accessors - -- [redis](Client.md#redis) - -### Methods - -- [close](Client.md#close) -- [fetchRepository](Client.md#fetchrepository) -- [isOpen](Client.md#isopen) -- [open](Client.md#open) -- [use](Client.md#use) -- [useNoClose](Client.md#usenoclose) - -## Constructors - -### constructor - -• **new Client**() - -## Accessors - -### redis - -• `get` **redis**(): `undefined` \| [`RedisConnection`](../README.md#redisconnection) - -Returns the underlying Node Redis connection being used. - -#### Returns - -`undefined` \| [`RedisConnection`](../README.md#redisconnection) - -#### Defined in - -[lib/client/client.ts:70](https://github.com/redis/redis-om-node/blob/5777b6c/lib/client/client.ts#L70) - -## Methods - -### close - -▸ **close**(): `Promise`<`void`\> - -Close the connection to Redis. - -#### Returns - -`Promise`<`void`\> - -#### Defined in - -[lib/client/client.ts:127](https://github.com/redis/redis-om-node/blob/5777b6c/lib/client/client.ts#L127) - -___ - -### fetchRepository - -▸ **fetchRepository**(`schema`): [`Repository`](Repository.md) - -Creates a repository for the given schema. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `schema` | [`Schema`](Schema.md) | The schema. | - -#### Returns - -[`Repository`](Repository.md) - -A repository for the provided schema. - -#### Defined in - -[lib/client/client.ts:119](https://github.com/redis/redis-om-node/blob/5777b6c/lib/client/client.ts#L119) - -___ - -### isOpen - -▸ **isOpen**(): `boolean` - -#### Returns - -`boolean` - -Whether a connection is already open. - -#### Defined in - -[lib/client/client.ts:207](https://github.com/redis/redis-om-node/blob/5777b6c/lib/client/client.ts#L207) - -___ - -### open - -▸ **open**(`url?`): `Promise`<[`Client`](Client.md)\> - -Open a connection to Redis at the provided URL. - -#### Parameters - -| Name | Type | Default value | Description | -| :------ | :------ | :------ | :------ | -| `url` | `string` | `'redis://localhost:6379'` | A URL to Redis as defined with the [IANA](https://www.iana.org/assignments/uri-schemes/prov/redis). | - -#### Returns - -`Promise`<[`Client`](Client.md)\> - -This [Client](Client.md) instance. - -#### Defined in - -[lib/client/client.ts:104](https://github.com/redis/redis-om-node/blob/5777b6c/lib/client/client.ts#L104) - -___ - -### use - -▸ **use**(`connection`): `Promise`<[`Client`](Client.md)\> - -Attaches an existing Node Redis connection to this Redis OM client. Closes -any existing connection. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `connection` | [`RedisConnection`](../README.md#redisconnection) | An existing Node Redis client. | - -#### Returns - -`Promise`<[`Client`](Client.md)\> - -This [Client](Client.md) instance. - -#### Defined in - -[lib/client/client.ts:81](https://github.com/redis/redis-om-node/blob/5777b6c/lib/client/client.ts#L81) - -___ - -### useNoClose - -▸ **useNoClose**(`connection`): [`Client`](Client.md) - -Attaches an existing Node Redis connection to this Redis OM client. Does -not close any existing connection. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `connection` | [`RedisConnection`](../README.md#redisconnection) | An existing Node Redis client. | - -#### Returns - -[`Client`](Client.md) - -This [Client](Client.md) instance. - -#### Defined in - -[lib/client/client.ts:93](https://github.com/redis/redis-om-node/blob/5777b6c/lib/client/client.ts#L93) diff --git a/docs/classes/Field.md b/docs/classes/Field.md deleted file mode 100644 index ff9b078..0000000 --- a/docs/classes/Field.md +++ /dev/null @@ -1,237 +0,0 @@ -[redis-om](../README.md) / Field - -# Class: Field - -Describes a field in a [Schema](Schema.md). - -## Table of contents - -### Constructors - -- [constructor](Field.md#constructor) - -### Accessors - -- [caseSensitive](Field.md#casesensitive) -- [hashField](Field.md#hashfield) -- [indexed](Field.md#indexed) -- [jsonPath](Field.md#jsonpath) -- [matcher](Field.md#matcher) -- [name](Field.md#name) -- [normalized](Field.md#normalized) -- [separator](Field.md#separator) -- [sortable](Field.md#sortable) -- [stemming](Field.md#stemming) -- [type](Field.md#type) -- [weight](Field.md#weight) - -## Constructors - -### constructor - -• **new Field**(`name`, `definition`) - -Creates a Field. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `name` | `string` | The name of the Field. | -| `definition` | [`FieldDefinition`](../README.md#fielddefinition) | The underlying [FieldDefinition](../README.md#fielddefinition). | - -#### Defined in - -[lib/schema/field.ts:17](https://github.com/redis/redis-om-node/blob/5777b6c/lib/schema/field.ts#L17) - -## Accessors - -### caseSensitive - -• `get` **caseSensitive**(): `boolean` - -The case-sensitivity of the field. - -#### Returns - -`boolean` - -#### Defined in - -[lib/schema/field.ts:55](https://github.com/redis/redis-om-node/blob/5777b6c/lib/schema/field.ts#L55) - -___ - -### hashField - -• `get` **hashField**(): `string` - -The field name used to store this [Field](Field.md) in a Hash. - -#### Returns - -`string` - -#### Defined in - -[lib/schema/field.ts:33](https://github.com/redis/redis-om-node/blob/5777b6c/lib/schema/field.ts#L33) - -___ - -### indexed - -• `get` **indexed**(): `boolean` - -Indicates the field as being indexed—and thus queryable—by RediSearch. - -#### Returns - -`boolean` - -#### Defined in - -[lib/schema/field.ts:60](https://github.com/redis/redis-om-node/blob/5777b6c/lib/schema/field.ts#L60) - -___ - -### jsonPath - -• `get` **jsonPath**(): `string` - -The JSONPath used to store this [Field](Field.md) in a JSON document. - -#### Returns - -`string` - -#### Defined in - -[lib/schema/field.ts:38](https://github.com/redis/redis-om-node/blob/5777b6c/lib/schema/field.ts#L38) - -___ - -### matcher - -• `get` **matcher**(): ``null`` \| `string` - -The phonetic matcher for the field. - -#### Returns - -``null`` \| `string` - -#### Defined in - -[lib/schema/field.ts:80](https://github.com/redis/redis-om-node/blob/5777b6c/lib/schema/field.ts#L80) - -___ - -### name - -• `get` **name**(): `string` - -The name of the field. - -#### Returns - -`string` - -#### Defined in - -[lib/schema/field.ts:23](https://github.com/redis/redis-om-node/blob/5777b6c/lib/schema/field.ts#L23) - -___ - -### normalized - -• `get` **normalized**(): `boolean` - -Indicates that the field is normalized. Ignored if sortable is false. - -#### Returns - -`boolean` - -#### Defined in - -[lib/schema/field.ts:70](https://github.com/redis/redis-om-node/blob/5777b6c/lib/schema/field.ts#L70) - -___ - -### separator - -• `get` **separator**(): `string` - -The separator for string[] fields when stored in Hashes. - -#### Returns - -`string` - -#### Defined in - -[lib/schema/field.ts:45](https://github.com/redis/redis-om-node/blob/5777b6c/lib/schema/field.ts#L45) - -___ - -### sortable - -• `get` **sortable**(): `boolean` - -Indicates that the field as sortable. - -#### Returns - -`boolean` - -#### Defined in - -[lib/schema/field.ts:50](https://github.com/redis/redis-om-node/blob/5777b6c/lib/schema/field.ts#L50) - -___ - -### stemming - -• `get` **stemming**(): `boolean` - -Indicates that the field as indexed with stemming support. - -#### Returns - -`boolean` - -#### Defined in - -[lib/schema/field.ts:65](https://github.com/redis/redis-om-node/blob/5777b6c/lib/schema/field.ts#L65) - -___ - -### type - -• `get` **type**(): [`FieldType`](../README.md#fieldtype) - -The [type](../README.md#fieldtype) of the field. - -#### Returns - -[`FieldType`](../README.md#fieldtype) - -#### Defined in - -[lib/schema/field.ts:28](https://github.com/redis/redis-om-node/blob/5777b6c/lib/schema/field.ts#L28) - -___ - -### weight - -• `get` **weight**(): ``null`` \| `number` - -The search weight of the field. - -#### Returns - -``null`` \| `number` - -#### Defined in - -[lib/schema/field.ts:75](https://github.com/redis/redis-om-node/blob/5777b6c/lib/schema/field.ts#L75) diff --git a/docs/classes/FieldNotInSchema.md b/docs/classes/FieldNotInSchema.md deleted file mode 100644 index 1e6780b..0000000 --- a/docs/classes/FieldNotInSchema.md +++ /dev/null @@ -1,198 +0,0 @@ -[redis-om](../README.md) / FieldNotInSchema - -# Class: FieldNotInSchema - -## Hierarchy - -- [`SearchError`](SearchError.md) - - ↳ **`FieldNotInSchema`** - -## Table of contents - -### Constructors - -- [constructor](FieldNotInSchema.md#constructor) - -### Properties - -- [cause](FieldNotInSchema.md#cause) -- [message](FieldNotInSchema.md#message) -- [name](FieldNotInSchema.md#name) -- [stack](FieldNotInSchema.md#stack) -- [prepareStackTrace](FieldNotInSchema.md#preparestacktrace) -- [stackTraceLimit](FieldNotInSchema.md#stacktracelimit) - -### Accessors - -- [field](FieldNotInSchema.md#field) - -### Methods - -- [captureStackTrace](FieldNotInSchema.md#capturestacktrace) - -## Constructors - -### constructor - -• **new FieldNotInSchema**(`fieldName`) - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `fieldName` | `string` | - -#### Overrides - -[SearchError](SearchError.md).[constructor](SearchError.md#constructor) - -#### Defined in - -[lib/error/search-error.ts:11](https://github.com/redis/redis-om-node/blob/5777b6c/lib/error/search-error.ts#L11) - -## Properties - -### cause - -• `Optional` **cause**: `unknown` - -#### Inherited from - -[SearchError](SearchError.md).[cause](SearchError.md#cause) - -#### Defined in - -node_modules/typescript/lib/lib.es2022.error.d.ts:26 - -___ - -### message - -• **message**: `string` - -#### Inherited from - -[SearchError](SearchError.md).[message](SearchError.md#message) - -#### Defined in - -node_modules/typescript/lib/lib.es5.d.ts:1054 - -___ - -### name - -• **name**: `string` - -#### Inherited from - -[SearchError](SearchError.md).[name](SearchError.md#name) - -#### Defined in - -node_modules/typescript/lib/lib.es5.d.ts:1053 - -___ - -### stack - -• `Optional` **stack**: `string` - -#### Inherited from - -[SearchError](SearchError.md).[stack](SearchError.md#stack) - -#### Defined in - -node_modules/typescript/lib/lib.es5.d.ts:1055 - -___ - -### prepareStackTrace - -▪ `Static` `Optional` **prepareStackTrace**: (`err`: `Error`, `stackTraces`: `CallSite`[]) => `any` - -#### Type declaration - -▸ (`err`, `stackTraces`): `any` - -Optional override for formatting stack traces - -**`See`** - -https://v8.dev/docs/stack-trace-api#customizing-stack-traces - -##### Parameters - -| Name | Type | -| :------ | :------ | -| `err` | `Error` | -| `stackTraces` | `CallSite`[] | - -##### Returns - -`any` - -#### Inherited from - -[SearchError](SearchError.md).[prepareStackTrace](SearchError.md#preparestacktrace) - -#### Defined in - -node_modules/@types/node/globals.d.ts:11 - -___ - -### stackTraceLimit - -▪ `Static` **stackTraceLimit**: `number` - -#### Inherited from - -[SearchError](SearchError.md).[stackTraceLimit](SearchError.md#stacktracelimit) - -#### Defined in - -node_modules/@types/node/globals.d.ts:13 - -## Accessors - -### field - -• `get` **field**(): `string` - -#### Returns - -`string` - -#### Defined in - -[lib/error/search-error.ts:16](https://github.com/redis/redis-om-node/blob/5777b6c/lib/error/search-error.ts#L16) - -## Methods - -### captureStackTrace - -▸ `Static` **captureStackTrace**(`targetObject`, `constructorOpt?`): `void` - -Create .stack property on a target object - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `targetObject` | `object` | -| `constructorOpt?` | `Function` | - -#### Returns - -`void` - -#### Inherited from - -[SearchError](SearchError.md).[captureStackTrace](SearchError.md#capturestacktrace) - -#### Defined in - -node_modules/@types/node/globals.d.ts:4 diff --git a/docs/classes/InvalidHashInput.md b/docs/classes/InvalidHashInput.md deleted file mode 100644 index e0b6c2a..0000000 --- a/docs/classes/InvalidHashInput.md +++ /dev/null @@ -1,213 +0,0 @@ -[redis-om](../README.md) / InvalidHashInput - -# Class: InvalidHashInput - -## Hierarchy - -- [`InvalidInput`](InvalidInput.md) - - ↳ **`InvalidHashInput`** - -## Table of contents - -### Constructors - -- [constructor](InvalidHashInput.md#constructor) - -### Properties - -- [cause](InvalidHashInput.md#cause) -- [message](InvalidHashInput.md#message) -- [name](InvalidHashInput.md#name) -- [stack](InvalidHashInput.md#stack) -- [prepareStackTrace](InvalidHashInput.md#preparestacktrace) -- [stackTraceLimit](InvalidHashInput.md#stacktracelimit) - -### Accessors - -- [fieldName](InvalidHashInput.md#fieldname) -- [fieldType](InvalidHashInput.md#fieldtype) - -### Methods - -- [captureStackTrace](InvalidHashInput.md#capturestacktrace) - -## Constructors - -### constructor - -• **new InvalidHashInput**(`field`) - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `field` | [`Field`](Field.md) | - -#### Overrides - -[InvalidInput](InvalidInput.md).[constructor](InvalidInput.md#constructor) - -#### Defined in - -[lib/error/invalid-input.ts:40](https://github.com/redis/redis-om-node/blob/5777b6c/lib/error/invalid-input.ts#L40) - -## Properties - -### cause - -• `Optional` **cause**: `unknown` - -#### Inherited from - -[InvalidInput](InvalidInput.md).[cause](InvalidInput.md#cause) - -#### Defined in - -node_modules/typescript/lib/lib.es2022.error.d.ts:26 - -___ - -### message - -• **message**: `string` - -#### Inherited from - -[InvalidInput](InvalidInput.md).[message](InvalidInput.md#message) - -#### Defined in - -node_modules/typescript/lib/lib.es5.d.ts:1054 - -___ - -### name - -• **name**: `string` - -#### Inherited from - -[InvalidInput](InvalidInput.md).[name](InvalidInput.md#name) - -#### Defined in - -node_modules/typescript/lib/lib.es5.d.ts:1053 - -___ - -### stack - -• `Optional` **stack**: `string` - -#### Inherited from - -[InvalidInput](InvalidInput.md).[stack](InvalidInput.md#stack) - -#### Defined in - -node_modules/typescript/lib/lib.es5.d.ts:1055 - -___ - -### prepareStackTrace - -▪ `Static` `Optional` **prepareStackTrace**: (`err`: `Error`, `stackTraces`: `CallSite`[]) => `any` - -#### Type declaration - -▸ (`err`, `stackTraces`): `any` - -Optional override for formatting stack traces - -**`See`** - -https://v8.dev/docs/stack-trace-api#customizing-stack-traces - -##### Parameters - -| Name | Type | -| :------ | :------ | -| `err` | `Error` | -| `stackTraces` | `CallSite`[] | - -##### Returns - -`any` - -#### Inherited from - -[InvalidInput](InvalidInput.md).[prepareStackTrace](InvalidInput.md#preparestacktrace) - -#### Defined in - -node_modules/@types/node/globals.d.ts:11 - -___ - -### stackTraceLimit - -▪ `Static` **stackTraceLimit**: `number` - -#### Inherited from - -[InvalidInput](InvalidInput.md).[stackTraceLimit](InvalidInput.md#stacktracelimit) - -#### Defined in - -node_modules/@types/node/globals.d.ts:13 - -## Accessors - -### fieldName - -• `get` **fieldName**(): `string` - -#### Returns - -`string` - -#### Defined in - -[lib/error/invalid-input.ts:46](https://github.com/redis/redis-om-node/blob/5777b6c/lib/error/invalid-input.ts#L46) - -___ - -### fieldType - -• `get` **fieldType**(): [`FieldType`](../README.md#fieldtype) - -#### Returns - -[`FieldType`](../README.md#fieldtype) - -#### Defined in - -[lib/error/invalid-input.ts:47](https://github.com/redis/redis-om-node/blob/5777b6c/lib/error/invalid-input.ts#L47) - -## Methods - -### captureStackTrace - -▸ `Static` **captureStackTrace**(`targetObject`, `constructorOpt?`): `void` - -Create .stack property on a target object - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `targetObject` | `object` | -| `constructorOpt?` | `Function` | - -#### Returns - -`void` - -#### Inherited from - -[InvalidInput](InvalidInput.md).[captureStackTrace](InvalidInput.md#capturestacktrace) - -#### Defined in - -node_modules/@types/node/globals.d.ts:4 diff --git a/docs/classes/InvalidHashValue.md b/docs/classes/InvalidHashValue.md deleted file mode 100644 index d66518c..0000000 --- a/docs/classes/InvalidHashValue.md +++ /dev/null @@ -1,228 +0,0 @@ -[redis-om](../README.md) / InvalidHashValue - -# Class: InvalidHashValue - -## Hierarchy - -- [`InvalidValue`](InvalidValue.md) - - ↳ **`InvalidHashValue`** - -## Table of contents - -### Constructors - -- [constructor](InvalidHashValue.md#constructor) - -### Properties - -- [cause](InvalidHashValue.md#cause) -- [message](InvalidHashValue.md#message) -- [name](InvalidHashValue.md#name) -- [stack](InvalidHashValue.md#stack) -- [prepareStackTrace](InvalidHashValue.md#preparestacktrace) -- [stackTraceLimit](InvalidHashValue.md#stacktracelimit) - -### Accessors - -- [fieldName](InvalidHashValue.md#fieldname) -- [fieldType](InvalidHashValue.md#fieldtype) -- [hashField](InvalidHashValue.md#hashfield) - -### Methods - -- [captureStackTrace](InvalidHashValue.md#capturestacktrace) - -## Constructors - -### constructor - -• **new InvalidHashValue**(`field`) - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `field` | [`Field`](Field.md) | - -#### Overrides - -[InvalidValue](InvalidValue.md).[constructor](InvalidValue.md#constructor) - -#### Defined in - -[lib/error/invalid-value.ts:40](https://github.com/redis/redis-om-node/blob/5777b6c/lib/error/invalid-value.ts#L40) - -## Properties - -### cause - -• `Optional` **cause**: `unknown` - -#### Inherited from - -[InvalidValue](InvalidValue.md).[cause](InvalidValue.md#cause) - -#### Defined in - -node_modules/typescript/lib/lib.es2022.error.d.ts:26 - -___ - -### message - -• **message**: `string` - -#### Inherited from - -[InvalidValue](InvalidValue.md).[message](InvalidValue.md#message) - -#### Defined in - -node_modules/typescript/lib/lib.es5.d.ts:1054 - -___ - -### name - -• **name**: `string` - -#### Inherited from - -[InvalidValue](InvalidValue.md).[name](InvalidValue.md#name) - -#### Defined in - -node_modules/typescript/lib/lib.es5.d.ts:1053 - -___ - -### stack - -• `Optional` **stack**: `string` - -#### Inherited from - -[InvalidValue](InvalidValue.md).[stack](InvalidValue.md#stack) - -#### Defined in - -node_modules/typescript/lib/lib.es5.d.ts:1055 - -___ - -### prepareStackTrace - -▪ `Static` `Optional` **prepareStackTrace**: (`err`: `Error`, `stackTraces`: `CallSite`[]) => `any` - -#### Type declaration - -▸ (`err`, `stackTraces`): `any` - -Optional override for formatting stack traces - -**`See`** - -https://v8.dev/docs/stack-trace-api#customizing-stack-traces - -##### Parameters - -| Name | Type | -| :------ | :------ | -| `err` | `Error` | -| `stackTraces` | `CallSite`[] | - -##### Returns - -`any` - -#### Inherited from - -[InvalidValue](InvalidValue.md).[prepareStackTrace](InvalidValue.md#preparestacktrace) - -#### Defined in - -node_modules/@types/node/globals.d.ts:11 - -___ - -### stackTraceLimit - -▪ `Static` **stackTraceLimit**: `number` - -#### Inherited from - -[InvalidValue](InvalidValue.md).[stackTraceLimit](InvalidValue.md#stacktracelimit) - -#### Defined in - -node_modules/@types/node/globals.d.ts:13 - -## Accessors - -### fieldName - -• `get` **fieldName**(): `string` - -#### Returns - -`string` - -#### Defined in - -[lib/error/invalid-value.ts:46](https://github.com/redis/redis-om-node/blob/5777b6c/lib/error/invalid-value.ts#L46) - -___ - -### fieldType - -• `get` **fieldType**(): [`FieldType`](../README.md#fieldtype) - -#### Returns - -[`FieldType`](../README.md#fieldtype) - -#### Defined in - -[lib/error/invalid-value.ts:47](https://github.com/redis/redis-om-node/blob/5777b6c/lib/error/invalid-value.ts#L47) - -___ - -### hashField - -• `get` **hashField**(): `string` - -#### Returns - -`string` - -#### Defined in - -[lib/error/invalid-value.ts:48](https://github.com/redis/redis-om-node/blob/5777b6c/lib/error/invalid-value.ts#L48) - -## Methods - -### captureStackTrace - -▸ `Static` **captureStackTrace**(`targetObject`, `constructorOpt?`): `void` - -Create .stack property on a target object - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `targetObject` | `object` | -| `constructorOpt?` | `Function` | - -#### Returns - -`void` - -#### Inherited from - -[InvalidValue](InvalidValue.md).[captureStackTrace](InvalidValue.md#capturestacktrace) - -#### Defined in - -node_modules/@types/node/globals.d.ts:4 diff --git a/docs/classes/InvalidInput.md b/docs/classes/InvalidInput.md deleted file mode 100644 index 9e96d3b..0000000 --- a/docs/classes/InvalidInput.md +++ /dev/null @@ -1,207 +0,0 @@ -[redis-om](../README.md) / InvalidInput - -# Class: InvalidInput - -## Hierarchy - -- [`RedisOmError`](RedisOmError.md) - - ↳ **`InvalidInput`** - - ↳↳ [`NullJsonInput`](NullJsonInput.md) - - ↳↳ [`InvalidJsonInput`](InvalidJsonInput.md) - - ↳↳ [`InvalidHashInput`](InvalidHashInput.md) - - ↳↳ [`NestedHashInput`](NestedHashInput.md) - - ↳↳ [`ArrayHashInput`](ArrayHashInput.md) - -## Table of contents - -### Constructors - -- [constructor](InvalidInput.md#constructor) - -### Properties - -- [cause](InvalidInput.md#cause) -- [message](InvalidInput.md#message) -- [name](InvalidInput.md#name) -- [stack](InvalidInput.md#stack) -- [prepareStackTrace](InvalidInput.md#preparestacktrace) -- [stackTraceLimit](InvalidInput.md#stacktracelimit) - -### Methods - -- [captureStackTrace](InvalidInput.md#capturestacktrace) - -## Constructors - -### constructor - -• **new InvalidInput**(`message?`) - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `message?` | `string` | - -#### Inherited from - -[RedisOmError](RedisOmError.md).[constructor](RedisOmError.md#constructor) - -#### Defined in - -node_modules/typescript/lib/lib.es5.d.ts:1059 - -• **new InvalidInput**(`message?`, `options?`) - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `message?` | `string` | -| `options?` | `ErrorOptions` | - -#### Inherited from - -[RedisOmError](RedisOmError.md).[constructor](RedisOmError.md#constructor) - -#### Defined in - -node_modules/typescript/lib/lib.es2022.error.d.ts:30 - -## Properties - -### cause - -• `Optional` **cause**: `unknown` - -#### Inherited from - -[RedisOmError](RedisOmError.md).[cause](RedisOmError.md#cause) - -#### Defined in - -node_modules/typescript/lib/lib.es2022.error.d.ts:26 - -___ - -### message - -• **message**: `string` - -#### Inherited from - -[RedisOmError](RedisOmError.md).[message](RedisOmError.md#message) - -#### Defined in - -node_modules/typescript/lib/lib.es5.d.ts:1054 - -___ - -### name - -• **name**: `string` - -#### Inherited from - -[RedisOmError](RedisOmError.md).[name](RedisOmError.md#name) - -#### Defined in - -node_modules/typescript/lib/lib.es5.d.ts:1053 - -___ - -### stack - -• `Optional` **stack**: `string` - -#### Inherited from - -[RedisOmError](RedisOmError.md).[stack](RedisOmError.md#stack) - -#### Defined in - -node_modules/typescript/lib/lib.es5.d.ts:1055 - -___ - -### prepareStackTrace - -▪ `Static` `Optional` **prepareStackTrace**: (`err`: `Error`, `stackTraces`: `CallSite`[]) => `any` - -#### Type declaration - -▸ (`err`, `stackTraces`): `any` - -Optional override for formatting stack traces - -**`See`** - -https://v8.dev/docs/stack-trace-api#customizing-stack-traces - -##### Parameters - -| Name | Type | -| :------ | :------ | -| `err` | `Error` | -| `stackTraces` | `CallSite`[] | - -##### Returns - -`any` - -#### Inherited from - -[RedisOmError](RedisOmError.md).[prepareStackTrace](RedisOmError.md#preparestacktrace) - -#### Defined in - -node_modules/@types/node/globals.d.ts:11 - -___ - -### stackTraceLimit - -▪ `Static` **stackTraceLimit**: `number` - -#### Inherited from - -[RedisOmError](RedisOmError.md).[stackTraceLimit](RedisOmError.md#stacktracelimit) - -#### Defined in - -node_modules/@types/node/globals.d.ts:13 - -## Methods - -### captureStackTrace - -▸ `Static` **captureStackTrace**(`targetObject`, `constructorOpt?`): `void` - -Create .stack property on a target object - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `targetObject` | `object` | -| `constructorOpt?` | `Function` | - -#### Returns - -`void` - -#### Inherited from - -[RedisOmError](RedisOmError.md).[captureStackTrace](RedisOmError.md#capturestacktrace) - -#### Defined in - -node_modules/@types/node/globals.d.ts:4 diff --git a/docs/classes/InvalidJsonInput.md b/docs/classes/InvalidJsonInput.md deleted file mode 100644 index 6d8b002..0000000 --- a/docs/classes/InvalidJsonInput.md +++ /dev/null @@ -1,228 +0,0 @@ -[redis-om](../README.md) / InvalidJsonInput - -# Class: InvalidJsonInput - -## Hierarchy - -- [`InvalidInput`](InvalidInput.md) - - ↳ **`InvalidJsonInput`** - -## Table of contents - -### Constructors - -- [constructor](InvalidJsonInput.md#constructor) - -### Properties - -- [cause](InvalidJsonInput.md#cause) -- [message](InvalidJsonInput.md#message) -- [name](InvalidJsonInput.md#name) -- [stack](InvalidJsonInput.md#stack) -- [prepareStackTrace](InvalidJsonInput.md#preparestacktrace) -- [stackTraceLimit](InvalidJsonInput.md#stacktracelimit) - -### Accessors - -- [fieldName](InvalidJsonInput.md#fieldname) -- [fieldType](InvalidJsonInput.md#fieldtype) -- [jsonPath](InvalidJsonInput.md#jsonpath) - -### Methods - -- [captureStackTrace](InvalidJsonInput.md#capturestacktrace) - -## Constructors - -### constructor - -• **new InvalidJsonInput**(`field`) - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `field` | [`Field`](Field.md) | - -#### Overrides - -[InvalidInput](InvalidInput.md).[constructor](InvalidInput.md#constructor) - -#### Defined in - -[lib/error/invalid-input.ts:25](https://github.com/redis/redis-om-node/blob/5777b6c/lib/error/invalid-input.ts#L25) - -## Properties - -### cause - -• `Optional` **cause**: `unknown` - -#### Inherited from - -[InvalidInput](InvalidInput.md).[cause](InvalidInput.md#cause) - -#### Defined in - -node_modules/typescript/lib/lib.es2022.error.d.ts:26 - -___ - -### message - -• **message**: `string` - -#### Inherited from - -[InvalidInput](InvalidInput.md).[message](InvalidInput.md#message) - -#### Defined in - -node_modules/typescript/lib/lib.es5.d.ts:1054 - -___ - -### name - -• **name**: `string` - -#### Inherited from - -[InvalidInput](InvalidInput.md).[name](InvalidInput.md#name) - -#### Defined in - -node_modules/typescript/lib/lib.es5.d.ts:1053 - -___ - -### stack - -• `Optional` **stack**: `string` - -#### Inherited from - -[InvalidInput](InvalidInput.md).[stack](InvalidInput.md#stack) - -#### Defined in - -node_modules/typescript/lib/lib.es5.d.ts:1055 - -___ - -### prepareStackTrace - -▪ `Static` `Optional` **prepareStackTrace**: (`err`: `Error`, `stackTraces`: `CallSite`[]) => `any` - -#### Type declaration - -▸ (`err`, `stackTraces`): `any` - -Optional override for formatting stack traces - -**`See`** - -https://v8.dev/docs/stack-trace-api#customizing-stack-traces - -##### Parameters - -| Name | Type | -| :------ | :------ | -| `err` | `Error` | -| `stackTraces` | `CallSite`[] | - -##### Returns - -`any` - -#### Inherited from - -[InvalidInput](InvalidInput.md).[prepareStackTrace](InvalidInput.md#preparestacktrace) - -#### Defined in - -node_modules/@types/node/globals.d.ts:11 - -___ - -### stackTraceLimit - -▪ `Static` **stackTraceLimit**: `number` - -#### Inherited from - -[InvalidInput](InvalidInput.md).[stackTraceLimit](InvalidInput.md#stacktracelimit) - -#### Defined in - -node_modules/@types/node/globals.d.ts:13 - -## Accessors - -### fieldName - -• `get` **fieldName**(): `string` - -#### Returns - -`string` - -#### Defined in - -[lib/error/invalid-input.ts:31](https://github.com/redis/redis-om-node/blob/5777b6c/lib/error/invalid-input.ts#L31) - -___ - -### fieldType - -• `get` **fieldType**(): [`FieldType`](../README.md#fieldtype) - -#### Returns - -[`FieldType`](../README.md#fieldtype) - -#### Defined in - -[lib/error/invalid-input.ts:32](https://github.com/redis/redis-om-node/blob/5777b6c/lib/error/invalid-input.ts#L32) - -___ - -### jsonPath - -• `get` **jsonPath**(): `string` - -#### Returns - -`string` - -#### Defined in - -[lib/error/invalid-input.ts:33](https://github.com/redis/redis-om-node/blob/5777b6c/lib/error/invalid-input.ts#L33) - -## Methods - -### captureStackTrace - -▸ `Static` **captureStackTrace**(`targetObject`, `constructorOpt?`): `void` - -Create .stack property on a target object - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `targetObject` | `object` | -| `constructorOpt?` | `Function` | - -#### Returns - -`void` - -#### Inherited from - -[InvalidInput](InvalidInput.md).[captureStackTrace](InvalidInput.md#capturestacktrace) - -#### Defined in - -node_modules/@types/node/globals.d.ts:4 diff --git a/docs/classes/InvalidJsonValue.md b/docs/classes/InvalidJsonValue.md deleted file mode 100644 index 637fbee..0000000 --- a/docs/classes/InvalidJsonValue.md +++ /dev/null @@ -1,228 +0,0 @@ -[redis-om](../README.md) / InvalidJsonValue - -# Class: InvalidJsonValue - -## Hierarchy - -- [`InvalidValue`](InvalidValue.md) - - ↳ **`InvalidJsonValue`** - -## Table of contents - -### Constructors - -- [constructor](InvalidJsonValue.md#constructor) - -### Properties - -- [cause](InvalidJsonValue.md#cause) -- [message](InvalidJsonValue.md#message) -- [name](InvalidJsonValue.md#name) -- [stack](InvalidJsonValue.md#stack) -- [prepareStackTrace](InvalidJsonValue.md#preparestacktrace) -- [stackTraceLimit](InvalidJsonValue.md#stacktracelimit) - -### Accessors - -- [fieldName](InvalidJsonValue.md#fieldname) -- [fieldType](InvalidJsonValue.md#fieldtype) -- [jsonPath](InvalidJsonValue.md#jsonpath) - -### Methods - -- [captureStackTrace](InvalidJsonValue.md#capturestacktrace) - -## Constructors - -### constructor - -• **new InvalidJsonValue**(`field`) - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `field` | [`Field`](Field.md) | - -#### Overrides - -[InvalidValue](InvalidValue.md).[constructor](InvalidValue.md#constructor) - -#### Defined in - -[lib/error/invalid-value.ts:25](https://github.com/redis/redis-om-node/blob/5777b6c/lib/error/invalid-value.ts#L25) - -## Properties - -### cause - -• `Optional` **cause**: `unknown` - -#### Inherited from - -[InvalidValue](InvalidValue.md).[cause](InvalidValue.md#cause) - -#### Defined in - -node_modules/typescript/lib/lib.es2022.error.d.ts:26 - -___ - -### message - -• **message**: `string` - -#### Inherited from - -[InvalidValue](InvalidValue.md).[message](InvalidValue.md#message) - -#### Defined in - -node_modules/typescript/lib/lib.es5.d.ts:1054 - -___ - -### name - -• **name**: `string` - -#### Inherited from - -[InvalidValue](InvalidValue.md).[name](InvalidValue.md#name) - -#### Defined in - -node_modules/typescript/lib/lib.es5.d.ts:1053 - -___ - -### stack - -• `Optional` **stack**: `string` - -#### Inherited from - -[InvalidValue](InvalidValue.md).[stack](InvalidValue.md#stack) - -#### Defined in - -node_modules/typescript/lib/lib.es5.d.ts:1055 - -___ - -### prepareStackTrace - -▪ `Static` `Optional` **prepareStackTrace**: (`err`: `Error`, `stackTraces`: `CallSite`[]) => `any` - -#### Type declaration - -▸ (`err`, `stackTraces`): `any` - -Optional override for formatting stack traces - -**`See`** - -https://v8.dev/docs/stack-trace-api#customizing-stack-traces - -##### Parameters - -| Name | Type | -| :------ | :------ | -| `err` | `Error` | -| `stackTraces` | `CallSite`[] | - -##### Returns - -`any` - -#### Inherited from - -[InvalidValue](InvalidValue.md).[prepareStackTrace](InvalidValue.md#preparestacktrace) - -#### Defined in - -node_modules/@types/node/globals.d.ts:11 - -___ - -### stackTraceLimit - -▪ `Static` **stackTraceLimit**: `number` - -#### Inherited from - -[InvalidValue](InvalidValue.md).[stackTraceLimit](InvalidValue.md#stacktracelimit) - -#### Defined in - -node_modules/@types/node/globals.d.ts:13 - -## Accessors - -### fieldName - -• `get` **fieldName**(): `string` - -#### Returns - -`string` - -#### Defined in - -[lib/error/invalid-value.ts:31](https://github.com/redis/redis-om-node/blob/5777b6c/lib/error/invalid-value.ts#L31) - -___ - -### fieldType - -• `get` **fieldType**(): [`FieldType`](../README.md#fieldtype) - -#### Returns - -[`FieldType`](../README.md#fieldtype) - -#### Defined in - -[lib/error/invalid-value.ts:32](https://github.com/redis/redis-om-node/blob/5777b6c/lib/error/invalid-value.ts#L32) - -___ - -### jsonPath - -• `get` **jsonPath**(): `string` - -#### Returns - -`string` - -#### Defined in - -[lib/error/invalid-value.ts:33](https://github.com/redis/redis-om-node/blob/5777b6c/lib/error/invalid-value.ts#L33) - -## Methods - -### captureStackTrace - -▸ `Static` **captureStackTrace**(`targetObject`, `constructorOpt?`): `void` - -Create .stack property on a target object - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `targetObject` | `object` | -| `constructorOpt?` | `Function` | - -#### Returns - -`void` - -#### Inherited from - -[InvalidValue](InvalidValue.md).[captureStackTrace](InvalidValue.md#capturestacktrace) - -#### Defined in - -node_modules/@types/node/globals.d.ts:4 diff --git a/docs/classes/InvalidSchema.md b/docs/classes/InvalidSchema.md deleted file mode 100644 index c54fa8e..0000000 --- a/docs/classes/InvalidSchema.md +++ /dev/null @@ -1,197 +0,0 @@ -[redis-om](../README.md) / InvalidSchema - -# Class: InvalidSchema - -## Hierarchy - -- [`RedisOmError`](RedisOmError.md) - - ↳ **`InvalidSchema`** - -## Table of contents - -### Constructors - -- [constructor](InvalidSchema.md#constructor) - -### Properties - -- [cause](InvalidSchema.md#cause) -- [message](InvalidSchema.md#message) -- [name](InvalidSchema.md#name) -- [stack](InvalidSchema.md#stack) -- [prepareStackTrace](InvalidSchema.md#preparestacktrace) -- [stackTraceLimit](InvalidSchema.md#stacktracelimit) - -### Methods - -- [captureStackTrace](InvalidSchema.md#capturestacktrace) - -## Constructors - -### constructor - -• **new InvalidSchema**(`message?`) - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `message?` | `string` | - -#### Inherited from - -[RedisOmError](RedisOmError.md).[constructor](RedisOmError.md#constructor) - -#### Defined in - -node_modules/typescript/lib/lib.es5.d.ts:1059 - -• **new InvalidSchema**(`message?`, `options?`) - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `message?` | `string` | -| `options?` | `ErrorOptions` | - -#### Inherited from - -[RedisOmError](RedisOmError.md).[constructor](RedisOmError.md#constructor) - -#### Defined in - -node_modules/typescript/lib/lib.es2022.error.d.ts:30 - -## Properties - -### cause - -• `Optional` **cause**: `unknown` - -#### Inherited from - -[RedisOmError](RedisOmError.md).[cause](RedisOmError.md#cause) - -#### Defined in - -node_modules/typescript/lib/lib.es2022.error.d.ts:26 - -___ - -### message - -• **message**: `string` - -#### Inherited from - -[RedisOmError](RedisOmError.md).[message](RedisOmError.md#message) - -#### Defined in - -node_modules/typescript/lib/lib.es5.d.ts:1054 - -___ - -### name - -• **name**: `string` - -#### Inherited from - -[RedisOmError](RedisOmError.md).[name](RedisOmError.md#name) - -#### Defined in - -node_modules/typescript/lib/lib.es5.d.ts:1053 - -___ - -### stack - -• `Optional` **stack**: `string` - -#### Inherited from - -[RedisOmError](RedisOmError.md).[stack](RedisOmError.md#stack) - -#### Defined in - -node_modules/typescript/lib/lib.es5.d.ts:1055 - -___ - -### prepareStackTrace - -▪ `Static` `Optional` **prepareStackTrace**: (`err`: `Error`, `stackTraces`: `CallSite`[]) => `any` - -#### Type declaration - -▸ (`err`, `stackTraces`): `any` - -Optional override for formatting stack traces - -**`See`** - -https://v8.dev/docs/stack-trace-api#customizing-stack-traces - -##### Parameters - -| Name | Type | -| :------ | :------ | -| `err` | `Error` | -| `stackTraces` | `CallSite`[] | - -##### Returns - -`any` - -#### Inherited from - -[RedisOmError](RedisOmError.md).[prepareStackTrace](RedisOmError.md#preparestacktrace) - -#### Defined in - -node_modules/@types/node/globals.d.ts:11 - -___ - -### stackTraceLimit - -▪ `Static` **stackTraceLimit**: `number` - -#### Inherited from - -[RedisOmError](RedisOmError.md).[stackTraceLimit](RedisOmError.md#stacktracelimit) - -#### Defined in - -node_modules/@types/node/globals.d.ts:13 - -## Methods - -### captureStackTrace - -▸ `Static` **captureStackTrace**(`targetObject`, `constructorOpt?`): `void` - -Create .stack property on a target object - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `targetObject` | `object` | -| `constructorOpt?` | `Function` | - -#### Returns - -`void` - -#### Inherited from - -[RedisOmError](RedisOmError.md).[captureStackTrace](RedisOmError.md#capturestacktrace) - -#### Defined in - -node_modules/@types/node/globals.d.ts:4 diff --git a/docs/classes/InvalidValue.md b/docs/classes/InvalidValue.md deleted file mode 100644 index 9634ce6..0000000 --- a/docs/classes/InvalidValue.md +++ /dev/null @@ -1,203 +0,0 @@ -[redis-om](../README.md) / InvalidValue - -# Class: InvalidValue - -## Hierarchy - -- [`RedisOmError`](RedisOmError.md) - - ↳ **`InvalidValue`** - - ↳↳ [`NullJsonValue`](NullJsonValue.md) - - ↳↳ [`InvalidJsonValue`](InvalidJsonValue.md) - - ↳↳ [`InvalidHashValue`](InvalidHashValue.md) - -## Table of contents - -### Constructors - -- [constructor](InvalidValue.md#constructor) - -### Properties - -- [cause](InvalidValue.md#cause) -- [message](InvalidValue.md#message) -- [name](InvalidValue.md#name) -- [stack](InvalidValue.md#stack) -- [prepareStackTrace](InvalidValue.md#preparestacktrace) -- [stackTraceLimit](InvalidValue.md#stacktracelimit) - -### Methods - -- [captureStackTrace](InvalidValue.md#capturestacktrace) - -## Constructors - -### constructor - -• **new InvalidValue**(`message?`) - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `message?` | `string` | - -#### Inherited from - -[RedisOmError](RedisOmError.md).[constructor](RedisOmError.md#constructor) - -#### Defined in - -node_modules/typescript/lib/lib.es5.d.ts:1059 - -• **new InvalidValue**(`message?`, `options?`) - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `message?` | `string` | -| `options?` | `ErrorOptions` | - -#### Inherited from - -[RedisOmError](RedisOmError.md).[constructor](RedisOmError.md#constructor) - -#### Defined in - -node_modules/typescript/lib/lib.es2022.error.d.ts:30 - -## Properties - -### cause - -• `Optional` **cause**: `unknown` - -#### Inherited from - -[RedisOmError](RedisOmError.md).[cause](RedisOmError.md#cause) - -#### Defined in - -node_modules/typescript/lib/lib.es2022.error.d.ts:26 - -___ - -### message - -• **message**: `string` - -#### Inherited from - -[RedisOmError](RedisOmError.md).[message](RedisOmError.md#message) - -#### Defined in - -node_modules/typescript/lib/lib.es5.d.ts:1054 - -___ - -### name - -• **name**: `string` - -#### Inherited from - -[RedisOmError](RedisOmError.md).[name](RedisOmError.md#name) - -#### Defined in - -node_modules/typescript/lib/lib.es5.d.ts:1053 - -___ - -### stack - -• `Optional` **stack**: `string` - -#### Inherited from - -[RedisOmError](RedisOmError.md).[stack](RedisOmError.md#stack) - -#### Defined in - -node_modules/typescript/lib/lib.es5.d.ts:1055 - -___ - -### prepareStackTrace - -▪ `Static` `Optional` **prepareStackTrace**: (`err`: `Error`, `stackTraces`: `CallSite`[]) => `any` - -#### Type declaration - -▸ (`err`, `stackTraces`): `any` - -Optional override for formatting stack traces - -**`See`** - -https://v8.dev/docs/stack-trace-api#customizing-stack-traces - -##### Parameters - -| Name | Type | -| :------ | :------ | -| `err` | `Error` | -| `stackTraces` | `CallSite`[] | - -##### Returns - -`any` - -#### Inherited from - -[RedisOmError](RedisOmError.md).[prepareStackTrace](RedisOmError.md#preparestacktrace) - -#### Defined in - -node_modules/@types/node/globals.d.ts:11 - -___ - -### stackTraceLimit - -▪ `Static` **stackTraceLimit**: `number` - -#### Inherited from - -[RedisOmError](RedisOmError.md).[stackTraceLimit](RedisOmError.md#stacktracelimit) - -#### Defined in - -node_modules/@types/node/globals.d.ts:13 - -## Methods - -### captureStackTrace - -▸ `Static` **captureStackTrace**(`targetObject`, `constructorOpt?`): `void` - -Create .stack property on a target object - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `targetObject` | `object` | -| `constructorOpt?` | `Function` | - -#### Returns - -`void` - -#### Inherited from - -[RedisOmError](RedisOmError.md).[captureStackTrace](RedisOmError.md#capturestacktrace) - -#### Defined in - -node_modules/@types/node/globals.d.ts:4 diff --git a/docs/classes/NestedHashInput.md b/docs/classes/NestedHashInput.md deleted file mode 100644 index 0d65c2d..0000000 --- a/docs/classes/NestedHashInput.md +++ /dev/null @@ -1,198 +0,0 @@ -[redis-om](../README.md) / NestedHashInput - -# Class: NestedHashInput - -## Hierarchy - -- [`InvalidInput`](InvalidInput.md) - - ↳ **`NestedHashInput`** - -## Table of contents - -### Constructors - -- [constructor](NestedHashInput.md#constructor) - -### Properties - -- [cause](NestedHashInput.md#cause) -- [message](NestedHashInput.md#message) -- [name](NestedHashInput.md#name) -- [stack](NestedHashInput.md#stack) -- [prepareStackTrace](NestedHashInput.md#preparestacktrace) -- [stackTraceLimit](NestedHashInput.md#stacktracelimit) - -### Accessors - -- [field](NestedHashInput.md#field) - -### Methods - -- [captureStackTrace](NestedHashInput.md#capturestacktrace) - -## Constructors - -### constructor - -• **new NestedHashInput**(`property`) - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `property` | `string` | - -#### Overrides - -[InvalidInput](InvalidInput.md).[constructor](InvalidInput.md#constructor) - -#### Defined in - -[lib/error/invalid-input.ts:54](https://github.com/redis/redis-om-node/blob/5777b6c/lib/error/invalid-input.ts#L54) - -## Properties - -### cause - -• `Optional` **cause**: `unknown` - -#### Inherited from - -[InvalidInput](InvalidInput.md).[cause](InvalidInput.md#cause) - -#### Defined in - -node_modules/typescript/lib/lib.es2022.error.d.ts:26 - -___ - -### message - -• **message**: `string` - -#### Inherited from - -[InvalidInput](InvalidInput.md).[message](InvalidInput.md#message) - -#### Defined in - -node_modules/typescript/lib/lib.es5.d.ts:1054 - -___ - -### name - -• **name**: `string` - -#### Inherited from - -[InvalidInput](InvalidInput.md).[name](InvalidInput.md#name) - -#### Defined in - -node_modules/typescript/lib/lib.es5.d.ts:1053 - -___ - -### stack - -• `Optional` **stack**: `string` - -#### Inherited from - -[InvalidInput](InvalidInput.md).[stack](InvalidInput.md#stack) - -#### Defined in - -node_modules/typescript/lib/lib.es5.d.ts:1055 - -___ - -### prepareStackTrace - -▪ `Static` `Optional` **prepareStackTrace**: (`err`: `Error`, `stackTraces`: `CallSite`[]) => `any` - -#### Type declaration - -▸ (`err`, `stackTraces`): `any` - -Optional override for formatting stack traces - -**`See`** - -https://v8.dev/docs/stack-trace-api#customizing-stack-traces - -##### Parameters - -| Name | Type | -| :------ | :------ | -| `err` | `Error` | -| `stackTraces` | `CallSite`[] | - -##### Returns - -`any` - -#### Inherited from - -[InvalidInput](InvalidInput.md).[prepareStackTrace](InvalidInput.md#preparestacktrace) - -#### Defined in - -node_modules/@types/node/globals.d.ts:11 - -___ - -### stackTraceLimit - -▪ `Static` **stackTraceLimit**: `number` - -#### Inherited from - -[InvalidInput](InvalidInput.md).[stackTraceLimit](InvalidInput.md#stacktracelimit) - -#### Defined in - -node_modules/@types/node/globals.d.ts:13 - -## Accessors - -### field - -• `get` **field**(): `string` - -#### Returns - -`string` - -#### Defined in - -[lib/error/invalid-input.ts:60](https://github.com/redis/redis-om-node/blob/5777b6c/lib/error/invalid-input.ts#L60) - -## Methods - -### captureStackTrace - -▸ `Static` **captureStackTrace**(`targetObject`, `constructorOpt?`): `void` - -Create .stack property on a target object - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `targetObject` | `object` | -| `constructorOpt?` | `Function` | - -#### Returns - -`void` - -#### Inherited from - -[InvalidInput](InvalidInput.md).[captureStackTrace](InvalidInput.md#capturestacktrace) - -#### Defined in - -node_modules/@types/node/globals.d.ts:4 diff --git a/docs/classes/NullJsonInput.md b/docs/classes/NullJsonInput.md deleted file mode 100644 index e701285..0000000 --- a/docs/classes/NullJsonInput.md +++ /dev/null @@ -1,228 +0,0 @@ -[redis-om](../README.md) / NullJsonInput - -# Class: NullJsonInput - -## Hierarchy - -- [`InvalidInput`](InvalidInput.md) - - ↳ **`NullJsonInput`** - -## Table of contents - -### Constructors - -- [constructor](NullJsonInput.md#constructor) - -### Properties - -- [cause](NullJsonInput.md#cause) -- [message](NullJsonInput.md#message) -- [name](NullJsonInput.md#name) -- [stack](NullJsonInput.md#stack) -- [prepareStackTrace](NullJsonInput.md#preparestacktrace) -- [stackTraceLimit](NullJsonInput.md#stacktracelimit) - -### Accessors - -- [fieldName](NullJsonInput.md#fieldname) -- [fieldType](NullJsonInput.md#fieldtype) -- [jsonPath](NullJsonInput.md#jsonpath) - -### Methods - -- [captureStackTrace](NullJsonInput.md#capturestacktrace) - -## Constructors - -### constructor - -• **new NullJsonInput**(`field`) - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `field` | [`Field`](Field.md) | - -#### Overrides - -[InvalidInput](InvalidInput.md).[constructor](InvalidInput.md#constructor) - -#### Defined in - -[lib/error/invalid-input.ts:10](https://github.com/redis/redis-om-node/blob/5777b6c/lib/error/invalid-input.ts#L10) - -## Properties - -### cause - -• `Optional` **cause**: `unknown` - -#### Inherited from - -[InvalidInput](InvalidInput.md).[cause](InvalidInput.md#cause) - -#### Defined in - -node_modules/typescript/lib/lib.es2022.error.d.ts:26 - -___ - -### message - -• **message**: `string` - -#### Inherited from - -[InvalidInput](InvalidInput.md).[message](InvalidInput.md#message) - -#### Defined in - -node_modules/typescript/lib/lib.es5.d.ts:1054 - -___ - -### name - -• **name**: `string` - -#### Inherited from - -[InvalidInput](InvalidInput.md).[name](InvalidInput.md#name) - -#### Defined in - -node_modules/typescript/lib/lib.es5.d.ts:1053 - -___ - -### stack - -• `Optional` **stack**: `string` - -#### Inherited from - -[InvalidInput](InvalidInput.md).[stack](InvalidInput.md#stack) - -#### Defined in - -node_modules/typescript/lib/lib.es5.d.ts:1055 - -___ - -### prepareStackTrace - -▪ `Static` `Optional` **prepareStackTrace**: (`err`: `Error`, `stackTraces`: `CallSite`[]) => `any` - -#### Type declaration - -▸ (`err`, `stackTraces`): `any` - -Optional override for formatting stack traces - -**`See`** - -https://v8.dev/docs/stack-trace-api#customizing-stack-traces - -##### Parameters - -| Name | Type | -| :------ | :------ | -| `err` | `Error` | -| `stackTraces` | `CallSite`[] | - -##### Returns - -`any` - -#### Inherited from - -[InvalidInput](InvalidInput.md).[prepareStackTrace](InvalidInput.md#preparestacktrace) - -#### Defined in - -node_modules/@types/node/globals.d.ts:11 - -___ - -### stackTraceLimit - -▪ `Static` **stackTraceLimit**: `number` - -#### Inherited from - -[InvalidInput](InvalidInput.md).[stackTraceLimit](InvalidInput.md#stacktracelimit) - -#### Defined in - -node_modules/@types/node/globals.d.ts:13 - -## Accessors - -### fieldName - -• `get` **fieldName**(): `string` - -#### Returns - -`string` - -#### Defined in - -[lib/error/invalid-input.ts:16](https://github.com/redis/redis-om-node/blob/5777b6c/lib/error/invalid-input.ts#L16) - -___ - -### fieldType - -• `get` **fieldType**(): [`FieldType`](../README.md#fieldtype) - -#### Returns - -[`FieldType`](../README.md#fieldtype) - -#### Defined in - -[lib/error/invalid-input.ts:17](https://github.com/redis/redis-om-node/blob/5777b6c/lib/error/invalid-input.ts#L17) - -___ - -### jsonPath - -• `get` **jsonPath**(): `string` - -#### Returns - -`string` - -#### Defined in - -[lib/error/invalid-input.ts:18](https://github.com/redis/redis-om-node/blob/5777b6c/lib/error/invalid-input.ts#L18) - -## Methods - -### captureStackTrace - -▸ `Static` **captureStackTrace**(`targetObject`, `constructorOpt?`): `void` - -Create .stack property on a target object - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `targetObject` | `object` | -| `constructorOpt?` | `Function` | - -#### Returns - -`void` - -#### Inherited from - -[InvalidInput](InvalidInput.md).[captureStackTrace](InvalidInput.md#capturestacktrace) - -#### Defined in - -node_modules/@types/node/globals.d.ts:4 diff --git a/docs/classes/NullJsonValue.md b/docs/classes/NullJsonValue.md deleted file mode 100644 index 0834d0e..0000000 --- a/docs/classes/NullJsonValue.md +++ /dev/null @@ -1,228 +0,0 @@ -[redis-om](../README.md) / NullJsonValue - -# Class: NullJsonValue - -## Hierarchy - -- [`InvalidValue`](InvalidValue.md) - - ↳ **`NullJsonValue`** - -## Table of contents - -### Constructors - -- [constructor](NullJsonValue.md#constructor) - -### Properties - -- [cause](NullJsonValue.md#cause) -- [message](NullJsonValue.md#message) -- [name](NullJsonValue.md#name) -- [stack](NullJsonValue.md#stack) -- [prepareStackTrace](NullJsonValue.md#preparestacktrace) -- [stackTraceLimit](NullJsonValue.md#stacktracelimit) - -### Accessors - -- [fieldName](NullJsonValue.md#fieldname) -- [fieldType](NullJsonValue.md#fieldtype) -- [jsonPath](NullJsonValue.md#jsonpath) - -### Methods - -- [captureStackTrace](NullJsonValue.md#capturestacktrace) - -## Constructors - -### constructor - -• **new NullJsonValue**(`field`) - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `field` | [`Field`](Field.md) | - -#### Overrides - -[InvalidValue](InvalidValue.md).[constructor](InvalidValue.md#constructor) - -#### Defined in - -[lib/error/invalid-value.ts:10](https://github.com/redis/redis-om-node/blob/5777b6c/lib/error/invalid-value.ts#L10) - -## Properties - -### cause - -• `Optional` **cause**: `unknown` - -#### Inherited from - -[InvalidValue](InvalidValue.md).[cause](InvalidValue.md#cause) - -#### Defined in - -node_modules/typescript/lib/lib.es2022.error.d.ts:26 - -___ - -### message - -• **message**: `string` - -#### Inherited from - -[InvalidValue](InvalidValue.md).[message](InvalidValue.md#message) - -#### Defined in - -node_modules/typescript/lib/lib.es5.d.ts:1054 - -___ - -### name - -• **name**: `string` - -#### Inherited from - -[InvalidValue](InvalidValue.md).[name](InvalidValue.md#name) - -#### Defined in - -node_modules/typescript/lib/lib.es5.d.ts:1053 - -___ - -### stack - -• `Optional` **stack**: `string` - -#### Inherited from - -[InvalidValue](InvalidValue.md).[stack](InvalidValue.md#stack) - -#### Defined in - -node_modules/typescript/lib/lib.es5.d.ts:1055 - -___ - -### prepareStackTrace - -▪ `Static` `Optional` **prepareStackTrace**: (`err`: `Error`, `stackTraces`: `CallSite`[]) => `any` - -#### Type declaration - -▸ (`err`, `stackTraces`): `any` - -Optional override for formatting stack traces - -**`See`** - -https://v8.dev/docs/stack-trace-api#customizing-stack-traces - -##### Parameters - -| Name | Type | -| :------ | :------ | -| `err` | `Error` | -| `stackTraces` | `CallSite`[] | - -##### Returns - -`any` - -#### Inherited from - -[InvalidValue](InvalidValue.md).[prepareStackTrace](InvalidValue.md#preparestacktrace) - -#### Defined in - -node_modules/@types/node/globals.d.ts:11 - -___ - -### stackTraceLimit - -▪ `Static` **stackTraceLimit**: `number` - -#### Inherited from - -[InvalidValue](InvalidValue.md).[stackTraceLimit](InvalidValue.md#stacktracelimit) - -#### Defined in - -node_modules/@types/node/globals.d.ts:13 - -## Accessors - -### fieldName - -• `get` **fieldName**(): `string` - -#### Returns - -`string` - -#### Defined in - -[lib/error/invalid-value.ts:16](https://github.com/redis/redis-om-node/blob/5777b6c/lib/error/invalid-value.ts#L16) - -___ - -### fieldType - -• `get` **fieldType**(): [`FieldType`](../README.md#fieldtype) - -#### Returns - -[`FieldType`](../README.md#fieldtype) - -#### Defined in - -[lib/error/invalid-value.ts:17](https://github.com/redis/redis-om-node/blob/5777b6c/lib/error/invalid-value.ts#L17) - -___ - -### jsonPath - -• `get` **jsonPath**(): `string` - -#### Returns - -`string` - -#### Defined in - -[lib/error/invalid-value.ts:18](https://github.com/redis/redis-om-node/blob/5777b6c/lib/error/invalid-value.ts#L18) - -## Methods - -### captureStackTrace - -▸ `Static` **captureStackTrace**(`targetObject`, `constructorOpt?`): `void` - -Create .stack property on a target object - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `targetObject` | `object` | -| `constructorOpt?` | `Function` | - -#### Returns - -`void` - -#### Inherited from - -[InvalidValue](InvalidValue.md).[captureStackTrace](InvalidValue.md#capturestacktrace) - -#### Defined in - -node_modules/@types/node/globals.d.ts:4 diff --git a/docs/classes/PointOutOfRange.md b/docs/classes/PointOutOfRange.md deleted file mode 100644 index 7d82ac9..0000000 --- a/docs/classes/PointOutOfRange.md +++ /dev/null @@ -1,203 +0,0 @@ -[redis-om](../README.md) / PointOutOfRange - -# Class: PointOutOfRange - -## Hierarchy - -- [`RedisOmError`](RedisOmError.md) - - ↳ **`PointOutOfRange`** - -## Table of contents - -### Constructors - -- [constructor](PointOutOfRange.md#constructor) - -### Properties - -- [cause](PointOutOfRange.md#cause) -- [message](PointOutOfRange.md#message) -- [name](PointOutOfRange.md#name) -- [stack](PointOutOfRange.md#stack) -- [prepareStackTrace](PointOutOfRange.md#preparestacktrace) -- [stackTraceLimit](PointOutOfRange.md#stacktracelimit) - -### Accessors - -- [point](PointOutOfRange.md#point) - -### Methods - -- [captureStackTrace](PointOutOfRange.md#capturestacktrace) - -## Constructors - -### constructor - -• **new PointOutOfRange**(`point`) - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `point` | [`Point`](../README.md#point) | - -#### Overrides - -[RedisOmError](RedisOmError.md).[constructor](RedisOmError.md#constructor) - -#### Defined in - -[lib/error/point-out-of-range.ts:9](https://github.com/redis/redis-om-node/blob/5777b6c/lib/error/point-out-of-range.ts#L9) - -## Properties - -### cause - -• `Optional` **cause**: `unknown` - -#### Inherited from - -[RedisOmError](RedisOmError.md).[cause](RedisOmError.md#cause) - -#### Defined in - -node_modules/typescript/lib/lib.es2022.error.d.ts:26 - -___ - -### message - -• **message**: `string` - -#### Inherited from - -[RedisOmError](RedisOmError.md).[message](RedisOmError.md#message) - -#### Defined in - -node_modules/typescript/lib/lib.es5.d.ts:1054 - -___ - -### name - -• **name**: `string` - -#### Inherited from - -[RedisOmError](RedisOmError.md).[name](RedisOmError.md#name) - -#### Defined in - -node_modules/typescript/lib/lib.es5.d.ts:1053 - -___ - -### stack - -• `Optional` **stack**: `string` - -#### Inherited from - -[RedisOmError](RedisOmError.md).[stack](RedisOmError.md#stack) - -#### Defined in - -node_modules/typescript/lib/lib.es5.d.ts:1055 - -___ - -### prepareStackTrace - -▪ `Static` `Optional` **prepareStackTrace**: (`err`: `Error`, `stackTraces`: `CallSite`[]) => `any` - -#### Type declaration - -▸ (`err`, `stackTraces`): `any` - -Optional override for formatting stack traces - -**`See`** - -https://v8.dev/docs/stack-trace-api#customizing-stack-traces - -##### Parameters - -| Name | Type | -| :------ | :------ | -| `err` | `Error` | -| `stackTraces` | `CallSite`[] | - -##### Returns - -`any` - -#### Inherited from - -[RedisOmError](RedisOmError.md).[prepareStackTrace](RedisOmError.md#preparestacktrace) - -#### Defined in - -node_modules/@types/node/globals.d.ts:11 - -___ - -### stackTraceLimit - -▪ `Static` **stackTraceLimit**: `number` - -#### Inherited from - -[RedisOmError](RedisOmError.md).[stackTraceLimit](RedisOmError.md#stacktracelimit) - -#### Defined in - -node_modules/@types/node/globals.d.ts:13 - -## Accessors - -### point - -• `get` **point**(): `Object` - -#### Returns - -`Object` - -| Name | Type | -| :------ | :------ | -| `latitude` | `number` | -| `longitude` | `number` | - -#### Defined in - -[lib/error/point-out-of-range.ts:15](https://github.com/redis/redis-om-node/blob/5777b6c/lib/error/point-out-of-range.ts#L15) - -## Methods - -### captureStackTrace - -▸ `Static` **captureStackTrace**(`targetObject`, `constructorOpt?`): `void` - -Create .stack property on a target object - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `targetObject` | `object` | -| `constructorOpt?` | `Function` | - -#### Returns - -`void` - -#### Inherited from - -[RedisOmError](RedisOmError.md).[captureStackTrace](RedisOmError.md#capturestacktrace) - -#### Defined in - -node_modules/@types/node/globals.d.ts:4 diff --git a/docs/classes/RawSearch.md b/docs/classes/RawSearch.md deleted file mode 100644 index 826aff7..0000000 --- a/docs/classes/RawSearch.md +++ /dev/null @@ -1,1063 +0,0 @@ -[redis-om](../README.md) / RawSearch - -# Class: RawSearch - -Entry point to raw search which allows using raw RediSearch queries -against Redis OM. Requires that RediSearch (and optionally RedisJSON) be -installed. - -**`Template`** - -The type of [Entity](../README.md#entity) being sought. - -## Hierarchy - -- [`AbstractSearch`](AbstractSearch.md) - - ↳ **`RawSearch`** - -## Table of contents - -### Accessors - -- [return](RawSearch.md#return) - -### Methods - -- [all](RawSearch.md#all) -- [allIds](RawSearch.md#allids) -- [allKeys](RawSearch.md#allkeys) -- [count](RawSearch.md#count) -- [first](RawSearch.md#first) -- [firstId](RawSearch.md#firstid) -- [firstKey](RawSearch.md#firstkey) -- [max](RawSearch.md#max) -- [maxId](RawSearch.md#maxid) -- [maxKey](RawSearch.md#maxkey) -- [min](RawSearch.md#min) -- [minId](RawSearch.md#minid) -- [minKey](RawSearch.md#minkey) -- [page](RawSearch.md#page) -- [pageOfIds](RawSearch.md#pageofids) -- [pageOfKeys](RawSearch.md#pageofkeys) -- [returnAll](RawSearch.md#returnall) -- [returnAllIds](RawSearch.md#returnallids) -- [returnAllKeys](RawSearch.md#returnallkeys) -- [returnCount](RawSearch.md#returncount) -- [returnFirst](RawSearch.md#returnfirst) -- [returnFirstId](RawSearch.md#returnfirstid) -- [returnFirstKey](RawSearch.md#returnfirstkey) -- [returnMax](RawSearch.md#returnmax) -- [returnMaxId](RawSearch.md#returnmaxid) -- [returnMaxKey](RawSearch.md#returnmaxkey) -- [returnMin](RawSearch.md#returnmin) -- [returnMinId](RawSearch.md#returnminid) -- [returnMinKey](RawSearch.md#returnminkey) -- [returnPage](RawSearch.md#returnpage) -- [returnPageOfIds](RawSearch.md#returnpageofids) -- [returnPageOfKeys](RawSearch.md#returnpageofkeys) -- [sortAsc](RawSearch.md#sortasc) -- [sortAscending](RawSearch.md#sortascending) -- [sortBy](RawSearch.md#sortby) -- [sortDesc](RawSearch.md#sortdesc) -- [sortDescending](RawSearch.md#sortdescending) - -## Accessors - -### return - -• `get` **return**(): [`AbstractSearch`](AbstractSearch.md) - -Returns the current instance. Syntactic sugar to make your code more fluent. - -#### Returns - -[`AbstractSearch`](AbstractSearch.md) - -this - -#### Inherited from - -AbstractSearch.return - -#### Defined in - -[lib/search/search.ts:308](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L308) - -## Methods - -### all - -▸ **all**(`options?`): `Promise`<[`Entity`](../README.md#entity)[]\> - -Returns all the [Entities](../README.md#entity) that match this query. This method -makes multiple calls to Redis until all the [Entities](../README.md#entity) are returned. -You can specify the batch size by setting the `pageSize` property on the -options: - -```typescript -const entities = await repository.search().returnAll({ pageSize: 100 }) -``` - -#### Parameters - -| Name | Type | Default value | Description | -| :------ | :------ | :------ | :------ | -| `options` | `Object` | `undefined` | Options for the call. | -| `options.pageSize` | `number` | `10` | Number of [Entities](../README.md#entity) returned per batch. | - -#### Returns - -`Promise`<[`Entity`](../README.md#entity)[]\> - -An array of [Entities](../README.md#entity) matching the query. - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[all](AbstractSearch.md#all) - -#### Defined in - -[lib/search/search.ts:264](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L264) - -___ - -### allIds - -▸ **allIds**(`options?`): `Promise`<`string`[]\> - -Returns all the entity IDs that match this query. This method -makes multiple calls to Redis until all the entity IDs are returned. -You can specify the batch size by setting the `pageSize` property on the -options: - -```typescript -const keys = await repository.search().returnAllIds({ pageSize: 100 }) -``` - -#### Parameters - -| Name | Type | Default value | Description | -| :------ | :------ | :------ | :------ | -| `options` | `Object` | `undefined` | Options for the call. | -| `options.pageSize` | `number` | `10` | Number of entity IDs returned per batch. | - -#### Returns - -`Promise`<`string`[]\> - -An array of entity IDs matching the query. - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[allIds](AbstractSearch.md#allids) - -#### Defined in - -[lib/search/search.ts:282](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L282) - -___ - -### allKeys - -▸ **allKeys**(`options?`): `Promise`<`string`[]\> - -Returns all the key names in Redis that match this query. This method -makes multiple calls to Redis until all the key names are returned. -You can specify the batch size by setting the `pageSize` property on the -options: - -```typescript -const keys = await repository.search().returnAllKeys({ pageSize: 100 }) -``` - -#### Parameters - -| Name | Type | Default value | Description | -| :------ | :------ | :------ | :------ | -| `options` | `Object` | `undefined` | Options for the call. | -| `options.pageSize` | `number` | `10` | Number of key names returned per batch. | - -#### Returns - -`Promise`<`string`[]\> - -An array of key names matching the query. - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[allKeys](AbstractSearch.md#allkeys) - -#### Defined in - -[lib/search/search.ts:300](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L300) - -___ - -### count - -▸ **count**(): `Promise`<`number`\> - -Returns the number of [Entities](../README.md#entity) that match this query. - -#### Returns - -`Promise`<`number`\> - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[count](AbstractSearch.md#count) - -#### Defined in - -[lib/search/search.ts:188](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L188) - -___ - -### first - -▸ **first**(): `Promise`<``null`` \| [`Entity`](../README.md#entity)\> - -Returns the first [Entity](../README.md#entity) that matches this query. - -#### Returns - -`Promise`<``null`` \| [`Entity`](../README.md#entity)\> - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[first](AbstractSearch.md#first) - -#### Defined in - -[lib/search/search.ts:229](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L229) - -___ - -### firstId - -▸ **firstId**(): `Promise`<``null`` \| `string`\> - -Returns the first entity ID that matches this query. - -#### Returns - -`Promise`<``null`` \| `string`\> - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[firstId](AbstractSearch.md#firstid) - -#### Defined in - -[lib/search/search.ts:237](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L237) - -___ - -### firstKey - -▸ **firstKey**(): `Promise`<``null`` \| `string`\> - -Returns the first key name that matches this query. - -#### Returns - -`Promise`<``null`` \| `string`\> - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[firstKey](AbstractSearch.md#firstkey) - -#### Defined in - -[lib/search/search.ts:245](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L245) - -___ - -### max - -▸ **max**(`field`): `Promise`<``null`` \| [`Entity`](../README.md#entity)\> - -Finds the [Entity](../README.md#entity) with the maximal value for a field. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `field` | `string` | The field with the maximal value. | - -#### Returns - -`Promise`<``null`` \| [`Entity`](../README.md#entity)\> - -The entity ID [Entity](../README.md#entity) with the maximal value - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[max](AbstractSearch.md#max) - -#### Defined in - -[lib/search/search.ts:162](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L162) - -___ - -### maxId - -▸ **maxId**(`field`): `Promise`<``null`` \| `string`\> - -Finds the entity ID with the maximal value for a field. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `field` | `string` | The field with the maximal value. | - -#### Returns - -`Promise`<``null`` \| `string`\> - -The entity ID with the maximal value - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[maxId](AbstractSearch.md#maxid) - -#### Defined in - -[lib/search/search.ts:171](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L171) - -___ - -### maxKey - -▸ **maxKey**(`field`): `Promise`<``null`` \| `string`\> - -Finds the key name in Redis with the maximal value for a field. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `field` | `string` | The field with the maximal value. | - -#### Returns - -`Promise`<``null`` \| `string`\> - -The key name with the maximal value - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[maxKey](AbstractSearch.md#maxkey) - -#### Defined in - -[lib/search/search.ts:180](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L180) - -___ - -### min - -▸ **min**(`field`): `Promise`<``null`` \| [`Entity`](../README.md#entity)\> - -Finds the [Entity](../README.md#entity) with the minimal value for a field. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `field` | `string` | The field with the minimal value. | - -#### Returns - -`Promise`<``null`` \| [`Entity`](../README.md#entity)\> - -The [Entity](../README.md#entity) with the minimal value - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[min](AbstractSearch.md#min) - -#### Defined in - -[lib/search/search.ts:135](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L135) - -___ - -### minId - -▸ **minId**(`field`): `Promise`<``null`` \| `string`\> - -Finds the entity ID with the minimal value for a field. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `field` | `string` | The field with the minimal value. | - -#### Returns - -`Promise`<``null`` \| `string`\> - -The entity ID with the minimal value - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[minId](AbstractSearch.md#minid) - -#### Defined in - -[lib/search/search.ts:144](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L144) - -___ - -### minKey - -▸ **minKey**(`field`): `Promise`<``null`` \| `string`\> - -Finds the key name in Redis with the minimal value for a field. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `field` | `string` | The field with the minimal value. | - -#### Returns - -`Promise`<``null`` \| `string`\> - -The key name with the minimal value - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[minKey](AbstractSearch.md#minkey) - -#### Defined in - -[lib/search/search.ts:153](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L153) - -___ - -### page - -▸ **page**(`offset`, `count`): `Promise`<[`Entity`](../README.md#entity)[]\> - -Returns a page of [Entities](../README.md#entity) that match this query. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `offset` | `number` | The offset for where to start returning [Entities](../README.md#entity). | -| `count` | `number` | The number of [Entities](../README.md#entity) to return. | - -#### Returns - -`Promise`<[`Entity`](../README.md#entity)[]\> - -An array of [Entities](../README.md#entity) matching the query. - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[page](AbstractSearch.md#page) - -#### Defined in - -[lib/search/search.ts:199](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L199) - -___ - -### pageOfIds - -▸ **pageOfIds**(`offset`, `count`): `Promise`<`string`[]\> - -Returns a page of entity IDs that match this query. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `offset` | `number` | The offset for where to start returning entity IDs. | -| `count` | `number` | The number of entity IDs to return. | - -#### Returns - -`Promise`<`string`[]\> - -An array of strings matching the query. - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[pageOfIds](AbstractSearch.md#pageofids) - -#### Defined in - -[lib/search/search.ts:210](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L210) - -___ - -### pageOfKeys - -▸ **pageOfKeys**(`offset`, `count`): `Promise`<`string`[]\> - -Returns a page of key names in Redis that match this query. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `offset` | `number` | The offset for where to start returning key names. | -| `count` | `number` | The number of key names to return. | - -#### Returns - -`Promise`<`string`[]\> - -An array of strings matching the query. - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[pageOfKeys](AbstractSearch.md#pageofkeys) - -#### Defined in - -[lib/search/search.ts:221](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L221) - -___ - -### returnAll - -▸ **returnAll**(`options?`): `Promise`<[`Entity`](../README.md#entity)[]\> - -Alias for [all](Search.md#all). - -#### Parameters - -| Name | Type | Default value | -| :------ | :------ | :------ | -| `options` | `Object` | `undefined` | -| `options.pageSize` | `number` | `10` | - -#### Returns - -`Promise`<[`Entity`](../README.md#entity)[]\> - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[returnAll](AbstractSearch.md#returnall) - -#### Defined in - -[lib/search/search.ts:406](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L406) - -___ - -### returnAllIds - -▸ **returnAllIds**(`options?`): `Promise`<`string`[]\> - -Alias for [allIds](Search.md#allids). - -#### Parameters - -| Name | Type | Default value | -| :------ | :------ | :------ | -| `options` | `Object` | `undefined` | -| `options.pageSize` | `number` | `10` | - -#### Returns - -`Promise`<`string`[]\> - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[returnAllIds](AbstractSearch.md#returnallids) - -#### Defined in - -[lib/search/search.ts:413](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L413) - -___ - -### returnAllKeys - -▸ **returnAllKeys**(`options?`): `Promise`<`string`[]\> - -Alias for [allKeys](Search.md#allkeys). - -#### Parameters - -| Name | Type | Default value | -| :------ | :------ | :------ | -| `options` | `Object` | `undefined` | -| `options.pageSize` | `number` | `10` | - -#### Returns - -`Promise`<`string`[]\> - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[returnAllKeys](AbstractSearch.md#returnallkeys) - -#### Defined in - -[lib/search/search.ts:420](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L420) - -___ - -### returnCount - -▸ **returnCount**(): `Promise`<`number`\> - -Alias for [count](Search.md#count). - -#### Returns - -`Promise`<`number`\> - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[returnCount](AbstractSearch.md#returncount) - -#### Defined in - -[lib/search/search.ts:357](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L357) - -___ - -### returnFirst - -▸ **returnFirst**(): `Promise`<``null`` \| [`Entity`](../README.md#entity)\> - -Alias for [first](Search.md#first). - -#### Returns - -`Promise`<``null`` \| [`Entity`](../README.md#entity)\> - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[returnFirst](AbstractSearch.md#returnfirst) - -#### Defined in - -[lib/search/search.ts:385](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L385) - -___ - -### returnFirstId - -▸ **returnFirstId**(): `Promise`<``null`` \| `string`\> - -Alias for [firstId](Search.md#firstid). - -#### Returns - -`Promise`<``null`` \| `string`\> - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[returnFirstId](AbstractSearch.md#returnfirstid) - -#### Defined in - -[lib/search/search.ts:392](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L392) - -___ - -### returnFirstKey - -▸ **returnFirstKey**(): `Promise`<``null`` \| `string`\> - -Alias for [firstKey](Search.md#firstkey). - -#### Returns - -`Promise`<``null`` \| `string`\> - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[returnFirstKey](AbstractSearch.md#returnfirstkey) - -#### Defined in - -[lib/search/search.ts:399](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L399) - -___ - -### returnMax - -▸ **returnMax**(`field`): `Promise`<``null`` \| [`Entity`](../README.md#entity)\> - -Alias for [max](Search.md#max). - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `field` | `string` | - -#### Returns - -`Promise`<``null`` \| [`Entity`](../README.md#entity)\> - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[returnMax](AbstractSearch.md#returnmax) - -#### Defined in - -[lib/search/search.ts:336](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L336) - -___ - -### returnMaxId - -▸ **returnMaxId**(`field`): `Promise`<``null`` \| `string`\> - -Alias for [maxId](Search.md#maxid). - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `field` | `string` | - -#### Returns - -`Promise`<``null`` \| `string`\> - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[returnMaxId](AbstractSearch.md#returnmaxid) - -#### Defined in - -[lib/search/search.ts:343](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L343) - -___ - -### returnMaxKey - -▸ **returnMaxKey**(`field`): `Promise`<``null`` \| `string`\> - -Alias for [maxKey](Search.md#maxkey). - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `field` | `string` | - -#### Returns - -`Promise`<``null`` \| `string`\> - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[returnMaxKey](AbstractSearch.md#returnmaxkey) - -#### Defined in - -[lib/search/search.ts:350](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L350) - -___ - -### returnMin - -▸ **returnMin**(`field`): `Promise`<``null`` \| [`Entity`](../README.md#entity)\> - -Alias for [min](Search.md#min). - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `field` | `string` | - -#### Returns - -`Promise`<``null`` \| [`Entity`](../README.md#entity)\> - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[returnMin](AbstractSearch.md#returnmin) - -#### Defined in - -[lib/search/search.ts:315](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L315) - -___ - -### returnMinId - -▸ **returnMinId**(`field`): `Promise`<``null`` \| `string`\> - -Alias for [minId](Search.md#minid). - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `field` | `string` | - -#### Returns - -`Promise`<``null`` \| `string`\> - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[returnMinId](AbstractSearch.md#returnminid) - -#### Defined in - -[lib/search/search.ts:322](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L322) - -___ - -### returnMinKey - -▸ **returnMinKey**(`field`): `Promise`<``null`` \| `string`\> - -Alias for [minKey](Search.md#minkey). - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `field` | `string` | - -#### Returns - -`Promise`<``null`` \| `string`\> - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[returnMinKey](AbstractSearch.md#returnminkey) - -#### Defined in - -[lib/search/search.ts:329](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L329) - -___ - -### returnPage - -▸ **returnPage**(`offset`, `count`): `Promise`<[`Entity`](../README.md#entity)[]\> - -Alias for [page](Search.md#page). - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `offset` | `number` | -| `count` | `number` | - -#### Returns - -`Promise`<[`Entity`](../README.md#entity)[]\> - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[returnPage](AbstractSearch.md#returnpage) - -#### Defined in - -[lib/search/search.ts:364](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L364) - -___ - -### returnPageOfIds - -▸ **returnPageOfIds**(`offset`, `count`): `Promise`<`string`[]\> - -Alias for [pageOfIds](Search.md#pageofids). - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `offset` | `number` | -| `count` | `number` | - -#### Returns - -`Promise`<`string`[]\> - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[returnPageOfIds](AbstractSearch.md#returnpageofids) - -#### Defined in - -[lib/search/search.ts:371](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L371) - -___ - -### returnPageOfKeys - -▸ **returnPageOfKeys**(`offset`, `count`): `Promise`<`string`[]\> - -Alias for [pageOfKeys](Search.md#pageofkeys). - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `offset` | `number` | -| `count` | `number` | - -#### Returns - -`Promise`<`string`[]\> - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[returnPageOfKeys](AbstractSearch.md#returnpageofkeys) - -#### Defined in - -[lib/search/search.ts:378](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L378) - -___ - -### sortAsc - -▸ **sortAsc**(`field`): [`AbstractSearch`](AbstractSearch.md) - -Alias for [sortAscending](Search.md#sortascending). - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `field` | `string` | - -#### Returns - -[`AbstractSearch`](AbstractSearch.md) - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[sortAsc](AbstractSearch.md#sortasc) - -#### Defined in - -[lib/search/search.ts:86](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L86) - -___ - -### sortAscending - -▸ **sortAscending**(`field`): [`AbstractSearch`](AbstractSearch.md) - -Applies an ascending sort to the query. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `field` | `string` | The field to sort by. | - -#### Returns - -[`AbstractSearch`](AbstractSearch.md) - -this - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[sortAscending](AbstractSearch.md#sortascending) - -#### Defined in - -[lib/search/search.ts:63](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L63) - -___ - -### sortBy - -▸ **sortBy**(`fieldName`, `order?`): [`AbstractSearch`](AbstractSearch.md) - -Applies sorting for the query. - -#### Parameters - -| Name | Type | Default value | Description | -| :------ | :------ | :------ | :------ | -| `fieldName` | `string` | `undefined` | - | -| `order` | ``"ASC"`` \| ``"DESC"`` | `'ASC'` | The order of returned [Entities](../README.md#entity) Defaults to `ASC` (ascending) if not specified | - -#### Returns - -[`AbstractSearch`](AbstractSearch.md) - -this - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[sortBy](AbstractSearch.md#sortby) - -#### Defined in - -[lib/search/search.ts:96](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L96) - -___ - -### sortDesc - -▸ **sortDesc**(`field`): [`AbstractSearch`](AbstractSearch.md) - -Alias for [sortDescending](Search.md#sortdescending). - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `field` | `string` | - -#### Returns - -[`AbstractSearch`](AbstractSearch.md) - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[sortDesc](AbstractSearch.md#sortdesc) - -#### Defined in - -[lib/search/search.ts:70](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L70) - -___ - -### sortDescending - -▸ **sortDescending**(`field`): [`AbstractSearch`](AbstractSearch.md) - -Applies a descending sort to the query. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `field` | `string` | The field to sort by. | - -#### Returns - -[`AbstractSearch`](AbstractSearch.md) - -this - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[sortDescending](AbstractSearch.md#sortdescending) - -#### Defined in - -[lib/search/search.ts:79](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L79) diff --git a/docs/classes/RedisOmError.md b/docs/classes/RedisOmError.md deleted file mode 100644 index a20e789..0000000 --- a/docs/classes/RedisOmError.md +++ /dev/null @@ -1,207 +0,0 @@ -[redis-om](../README.md) / RedisOmError - -# Class: RedisOmError - -## Hierarchy - -- `Error` - - ↳ **`RedisOmError`** - - ↳↳ [`InvalidInput`](InvalidInput.md) - - ↳↳ [`InvalidSchema`](InvalidSchema.md) - - ↳↳ [`InvalidValue`](InvalidValue.md) - - ↳↳ [`PointOutOfRange`](PointOutOfRange.md) - - ↳↳ [`SearchError`](SearchError.md) - -## Table of contents - -### Constructors - -- [constructor](RedisOmError.md#constructor) - -### Properties - -- [cause](RedisOmError.md#cause) -- [message](RedisOmError.md#message) -- [name](RedisOmError.md#name) -- [stack](RedisOmError.md#stack) -- [prepareStackTrace](RedisOmError.md#preparestacktrace) -- [stackTraceLimit](RedisOmError.md#stacktracelimit) - -### Methods - -- [captureStackTrace](RedisOmError.md#capturestacktrace) - -## Constructors - -### constructor - -• **new RedisOmError**(`message?`) - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `message?` | `string` | - -#### Inherited from - -Error.constructor - -#### Defined in - -node_modules/typescript/lib/lib.es5.d.ts:1059 - -• **new RedisOmError**(`message?`, `options?`) - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `message?` | `string` | -| `options?` | `ErrorOptions` | - -#### Inherited from - -Error.constructor - -#### Defined in - -node_modules/typescript/lib/lib.es2022.error.d.ts:30 - -## Properties - -### cause - -• `Optional` **cause**: `unknown` - -#### Inherited from - -Error.cause - -#### Defined in - -node_modules/typescript/lib/lib.es2022.error.d.ts:26 - -___ - -### message - -• **message**: `string` - -#### Inherited from - -Error.message - -#### Defined in - -node_modules/typescript/lib/lib.es5.d.ts:1054 - -___ - -### name - -• **name**: `string` - -#### Inherited from - -Error.name - -#### Defined in - -node_modules/typescript/lib/lib.es5.d.ts:1053 - -___ - -### stack - -• `Optional` **stack**: `string` - -#### Inherited from - -Error.stack - -#### Defined in - -node_modules/typescript/lib/lib.es5.d.ts:1055 - -___ - -### prepareStackTrace - -▪ `Static` `Optional` **prepareStackTrace**: (`err`: `Error`, `stackTraces`: `CallSite`[]) => `any` - -#### Type declaration - -▸ (`err`, `stackTraces`): `any` - -Optional override for formatting stack traces - -**`See`** - -https://v8.dev/docs/stack-trace-api#customizing-stack-traces - -##### Parameters - -| Name | Type | -| :------ | :------ | -| `err` | `Error` | -| `stackTraces` | `CallSite`[] | - -##### Returns - -`any` - -#### Inherited from - -Error.prepareStackTrace - -#### Defined in - -node_modules/@types/node/globals.d.ts:11 - -___ - -### stackTraceLimit - -▪ `Static` **stackTraceLimit**: `number` - -#### Inherited from - -Error.stackTraceLimit - -#### Defined in - -node_modules/@types/node/globals.d.ts:13 - -## Methods - -### captureStackTrace - -▸ `Static` **captureStackTrace**(`targetObject`, `constructorOpt?`): `void` - -Create .stack property on a target object - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `targetObject` | `object` | -| `constructorOpt?` | `Function` | - -#### Returns - -`void` - -#### Inherited from - -Error.captureStackTrace - -#### Defined in - -node_modules/@types/node/globals.d.ts:4 diff --git a/docs/classes/Repository.md b/docs/classes/Repository.md deleted file mode 100644 index 8e45a31..0000000 --- a/docs/classes/Repository.md +++ /dev/null @@ -1,380 +0,0 @@ -[redis-om](../README.md) / Repository - -# Class: Repository - -A repository is the main interaction point for reading, writing, and -removing [Entities](../README.md#entity) from Redis. Create one by calling -[fetchRepository](Client.md#fetchrepository) and passing in a [Schema](Schema.md). Then -use the [fetch](Repository.md#fetch), [save](Repository.md#save), and -[remove](Repository.md#remove) methods to manage your data: - -```typescript -const repository = client.fetchRepository(schema) - -const foo = await repository.fetch('01FK6TCJBDK41RJ766A4SBWDJ9') -foo.aString = 'bar' -foo.aBoolean = false -await repository.save(foo) -``` - -Use the repository to create a new instance of an [Entity](../README.md#entity) -before you save it: - -```typescript -const foo = await repository.createEntity() -foo.aString = 'bar' -foo.aBoolean = false -await repository.save(foo) -``` - -If you want to use the [search](Repository.md#search) method, you need to create an index -first, and you need RediSearch or RedisJSON installed on your instance of Redis: - -```typescript -await repository.createIndex() -const entities = await repository.search() - .where('aString').eq('bar') - .and('aBoolean').is.false().returnAll() -``` - -## Table of contents - -### Constructors - -- [constructor](Repository.md#constructor) - -### Methods - -- [createIndex](Repository.md#createindex) -- [dropIndex](Repository.md#dropindex) -- [expire](Repository.md#expire) -- [fetch](Repository.md#fetch) -- [remove](Repository.md#remove) -- [save](Repository.md#save) -- [search](Repository.md#search) -- [searchRaw](Repository.md#searchraw) - -## Constructors - -### constructor - -• **new Repository**(`schema`, `clientOrConnection`) - -Creates a new [Repository](Repository.md). - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `schema` | [`Schema`](Schema.md) | The schema defining that data in the repository. | -| `clientOrConnection` | [`Client`](Client.md) \| [`RedisConnection`](../README.md#redisconnection) | - | - -#### Defined in - -[lib/repository/repository.ts:56](https://github.com/redis/redis-om-node/blob/5777b6c/lib/repository/repository.ts#L56) - -## Methods - -### createIndex - -▸ **createIndex**(): `Promise`<`void`\> - -Creates an index in Redis for use by the [search](Repository.md#search) method. -Does not create a new index if the index hasn't changed. Requires that -RediSearch and RedisJSON are installed on your instance of Redis. - -#### Returns - -`Promise`<`void`\> - -#### Defined in - -[lib/repository/repository.ts:71](https://github.com/redis/redis-om-node/blob/5777b6c/lib/repository/repository.ts#L71) - -___ - -### dropIndex - -▸ **dropIndex**(): `Promise`<`void`\> - -Removes an existing index from Redis. Use this method if you want to swap out your index -because your [Entity](../README.md#entity) has changed. Requires that RediSearch and RedisJSON are installed -on your instance of Redis. - -#### Returns - -`Promise`<`void`\> - -#### Defined in - -[lib/repository/repository.ts:109](https://github.com/redis/redis-om-node/blob/5777b6c/lib/repository/repository.ts#L109) - -___ - -### expire - -▸ **expire**(`id`, `ttlInSeconds`): `Promise`<`void`\> - -Set the time to live of the [Entity](../README.md#entity). If the [Entity](../README.md#entity) is not -found, does nothing. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `id` | `string` | The ID of the [Entity](../README.md#entity) to set and expiration for. | -| `ttlInSeconds` | `number` | The time to live in seconds. | - -#### Returns - -`Promise`<`void`\> - -#### Defined in - -[lib/repository/repository.ts:242](https://github.com/redis/redis-om-node/blob/5777b6c/lib/repository/repository.ts#L242) - -▸ **expire**(`ids`, `ttlInSeconds`): `Promise`<`void`\> - -Set the time to live of the [Entities](../README.md#entity) in Redis with the given -ids. If a particular [Entity](../README.md#entity) is not found, does nothing. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `ids` | `string`[] | The IDs of the [Entities](../README.md#entity) you wish to delete. | -| `ttlInSeconds` | `number` | - | - -#### Returns - -`Promise`<`void`\> - -#### Defined in - -[lib/repository/repository.ts:250](https://github.com/redis/redis-om-node/blob/5777b6c/lib/repository/repository.ts#L250) - -___ - -### fetch - -▸ **fetch**(`id`): `Promise`<[`Entity`](../README.md#entity)\> - -Read and return an [Entity](../README.md#entity) from Redis for the given id. If -the [Entity](../README.md#entity) is not found, returns an empty [Entity](../README.md#entity). - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `id` | `string` | The ID of the [Entity](../README.md#entity) you seek. | - -#### Returns - -`Promise`<[`Entity`](../README.md#entity)\> - -The matching Entity. - -#### Defined in - -[lib/repository/repository.ts:171](https://github.com/redis/redis-om-node/blob/5777b6c/lib/repository/repository.ts#L171) - -▸ **fetch**(`...ids`): `Promise`<[`Entity`](../README.md#entity)[]\> - -Read and return the [Entities](../README.md#entity) from Redis with the given IDs. If -a particular [Entity](../README.md#entity) is not found, returns that [Entity](../README.md#entity) as empty. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `...ids` | `string`[] | The IDs of the [Entities](../README.md#entity) you seek. | - -#### Returns - -`Promise`<[`Entity`](../README.md#entity)[]\> - -The matching Entities. - -#### Defined in - -[lib/repository/repository.ts:180](https://github.com/redis/redis-om-node/blob/5777b6c/lib/repository/repository.ts#L180) - -▸ **fetch**(`ids`): `Promise`<[`Entity`](../README.md#entity)[]\> - -Read and return the [Entities](../README.md#entity) from Redis with the given IDs. If -a particular [Entity](../README.md#entity) is not found, returns that [Entity](../README.md#entity) as empty. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `ids` | `string`[] | The IDs of the [Entities](../README.md#entity) you seek. | - -#### Returns - -`Promise`<[`Entity`](../README.md#entity)[]\> - -The matching Entities. - -#### Defined in - -[lib/repository/repository.ts:189](https://github.com/redis/redis-om-node/blob/5777b6c/lib/repository/repository.ts#L189) - -___ - -### remove - -▸ **remove**(`id`): `Promise`<`void`\> - -Remove an [Entity](../README.md#entity) from Redis for the given id. If the [Entity](../README.md#entity) is -not found, does nothing. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `id` | `string` | The ID of the [Entity](../README.md#entity) you wish to delete. | - -#### Returns - -`Promise`<`void`\> - -#### Defined in - -[lib/repository/repository.ts:205](https://github.com/redis/redis-om-node/blob/5777b6c/lib/repository/repository.ts#L205) - -▸ **remove**(`...ids`): `Promise`<`void`\> - -Remove the [Entities](../README.md#entity) from Redis for the given ids. If a -particular [Entity](../README.md#entity) is not found, does nothing. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `...ids` | `string`[] | The IDs of the [Entities](../README.md#entity) you wish to delete. | - -#### Returns - -`Promise`<`void`\> - -#### Defined in - -[lib/repository/repository.ts:213](https://github.com/redis/redis-om-node/blob/5777b6c/lib/repository/repository.ts#L213) - -▸ **remove**(`ids`): `Promise`<`void`\> - -Remove the [Entities](../README.md#entity) from Redis for the given ids. If a -particular [Entity](../README.md#entity) is not found, does nothing. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `ids` | `string`[] | The IDs of the [Entities](../README.md#entity) you wish to delete. | - -#### Returns - -`Promise`<`void`\> - -#### Defined in - -[lib/repository/repository.ts:221](https://github.com/redis/redis-om-node/blob/5777b6c/lib/repository/repository.ts#L221) - -___ - -### save - -▸ **save**(`entity`): `Promise`<[`Entity`](../README.md#entity)\> - -Insert or update an [Entity](../README.md#entity) to Redis using its entityId property -if present. If it's not, one is generated. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `entity` | [`Entity`](../README.md#entity) | The Entity to save. | - -#### Returns - -`Promise`<[`Entity`](../README.md#entity)\> - -A copy of the provided Entity with EntityId and EntityKeyName properties added. - -#### Defined in - -[lib/repository/repository.ts:134](https://github.com/redis/redis-om-node/blob/5777b6c/lib/repository/repository.ts#L134) - -▸ **save**(`id`, `entity`): `Promise`<[`Entity`](../README.md#entity)\> - -Insert or update the [Entity](../README.md#entity) to Redis using the provided entityId. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `id` | `string` | The id to save the Entity under. | -| `entity` | [`Entity`](../README.md#entity) | The Entity to save. | - -#### Returns - -`Promise`<[`Entity`](../README.md#entity)\> - -A copy of the provided Entity with EntityId and EntityKeyName properties added. - -#### Defined in - -[lib/repository/repository.ts:143](https://github.com/redis/redis-om-node/blob/5777b6c/lib/repository/repository.ts#L143) - -___ - -### search - -▸ **search**(): [`Search`](Search.md) - -Kicks off the process of building a query. Requires that RediSearch (and optionally -RedisJSON) be installed on your instance of Redis. - -#### Returns - -[`Search`](Search.md) - -A [Search](Search.md) object. - -#### Defined in - -[lib/repository/repository.ts:268](https://github.com/redis/redis-om-node/blob/5777b6c/lib/repository/repository.ts#L268) - -___ - -### searchRaw - -▸ **searchRaw**(`query`): [`RawSearch`](RawSearch.md) - -Creates a search that bypasses Redis OM and instead allows you to execute a raw -RediSearch query. Requires that RediSearch (and optionally RedisJSON) be installed -on your instance of Redis. - -Refer to https://redis.io/docs/stack/search/reference/query_syntax/ for details on -RediSearch query syntax. - -**`Query`** - -The raw RediSearch query you want to rune. - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `query` | `string` | - -#### Returns - -[`RawSearch`](RawSearch.md) - -A [RawSearch](RawSearch.md) object. - -#### Defined in - -[lib/repository/repository.ts:283](https://github.com/redis/redis-om-node/blob/5777b6c/lib/repository/repository.ts#L283) diff --git a/docs/classes/Schema.md b/docs/classes/Schema.md deleted file mode 100644 index 77edbbc..0000000 --- a/docs/classes/Schema.md +++ /dev/null @@ -1,242 +0,0 @@ -[redis-om](../README.md) / Schema - -# Class: Schema - -Defines a schema that determines how an [Entity](../README.md#entity) is mapped -to Redis data structures. Construct by passing in a schema name, -a [SchemaDefinition](../README.md#schemadefinition), and optionally [SchemaOptions](../README.md#schemaoptions): - -```typescript -const schema = new Schema('foo', { - aString: { type: 'string' }, - aNumber: { type: 'number' }, - aBoolean: { type: 'boolean' }, - someText: { type: 'text' }, - aPoint: { type: 'point' }, - aDate: { type: 'date' }, - someStrings: { type: 'string[]' } -}, { - dataStructure: 'HASH' -}) -``` - -A Schema is primarily used by a [Repository](Repository.md) which requires a Schema in -its constructor. - -## Table of contents - -### Constructors - -- [constructor](Schema.md#constructor) - -### Accessors - -- [dataStructure](Schema.md#datastructure) -- [fields](Schema.md#fields) -- [indexHash](Schema.md#indexhash) -- [indexHashName](Schema.md#indexhashname) -- [indexName](Schema.md#indexname) -- [schemaName](Schema.md#schemaname) -- [stopWords](Schema.md#stopwords) -- [useStopWords](Schema.md#usestopwords) - -### Methods - -- [fieldByName](Schema.md#fieldbyname) -- [generateId](Schema.md#generateid) - -## Constructors - -### constructor - -• **new Schema**(`schemaName`, `schemaDef`, `options?`) - -Constructs a Schema. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `schemaName` | `string` | The name of the schema. Prefixes the ID when creating Redis keys. | -| `schemaDef` | [`SchemaDefinition`](../README.md#schemadefinition) | Defines all of the fields for the Schema and how they are mapped to Redis. | -| `options?` | [`SchemaOptions`](../README.md#schemaoptions) | Additional options for this Schema. | - -#### Defined in - -[lib/schema/schema.ts:49](https://github.com/redis/redis-om-node/blob/5777b6c/lib/schema/schema.ts#L49) - -## Accessors - -### dataStructure - -• `get` **dataStructure**(): [`DataStructure`](../README.md#datastructure) - -The configured data structure, a string with the value of either `HASH` or `JSON`, -that this Schema uses to store [Entities](../README.md#entity) in Redis. - -#### Returns - -[`DataStructure`](../README.md#datastructure) - -#### Defined in - -[lib/schema/schema.ts:92](https://github.com/redis/redis-om-node/blob/5777b6c/lib/schema/schema.ts#L92) - -___ - -### fields - -• `get` **fields**(): [`Field`](Field.md)[] - -The [Fields](Field.md) defined by this Schema. - -#### Returns - -[`Field`](Field.md)[] - -#### Defined in - -[lib/schema/schema.ts:68](https://github.com/redis/redis-om-node/blob/5777b6c/lib/schema/schema.ts#L68) - -___ - -### indexHash - -• `get` **indexHash**(): `string` - -A hash for this Schema that is used to determine if the Schema has been -changed when calling [createIndex](Repository.md#createindex). - -#### Returns - -`string` - -#### Defined in - -[lib/schema/schema.ts:120](https://github.com/redis/redis-om-node/blob/5777b6c/lib/schema/schema.ts#L120) - -___ - -### indexHashName - -• `get` **indexHashName**(): `string` - -The configured name for the RediSearch index hash for this Schema. - -#### Returns - -`string` - -#### Defined in - -[lib/schema/schema.ts:86](https://github.com/redis/redis-om-node/blob/5777b6c/lib/schema/schema.ts#L86) - -___ - -### indexName - -• `get` **indexName**(): `string` - -The configured name for the RediSearch index for this Schema. - -#### Returns - -`string` - -#### Defined in - -[lib/schema/schema.ts:83](https://github.com/redis/redis-om-node/blob/5777b6c/lib/schema/schema.ts#L83) - -___ - -### schemaName - -• `get` **schemaName**(): `string` - -The name of the schema. Prefixes the ID when creating Redis keys. Combined -with the results of idStrategy to generate a key. If name is `foo` and -idStrategy returns `12345` then the generated key would be `foo:12345`. - -#### Returns - -`string` - -#### Defined in - -[lib/schema/schema.ts:63](https://github.com/redis/redis-om-node/blob/5777b6c/lib/schema/schema.ts#L63) - -___ - -### stopWords - -• `get` **stopWords**(): `string`[] - -The configured stop words. Ignored if [useStopWords](Schema.md#usestopwords) is anything other -than `CUSTOM`. - -#### Returns - -`string`[] - -#### Defined in - -[lib/schema/schema.ts:104](https://github.com/redis/redis-om-node/blob/5777b6c/lib/schema/schema.ts#L104) - -___ - -### useStopWords - -• `get` **useStopWords**(): [`StopWordOptions`](../README.md#stopwordoptions) - -The configured usage of stop words, a string with the value of either `OFF`, `DEFAULT`, -or `CUSTOM`. See [SchemaOptions](../README.md#schemaoptions) for more details. - -#### Returns - -[`StopWordOptions`](../README.md#stopwordoptions) - -#### Defined in - -[lib/schema/schema.ts:98](https://github.com/redis/redis-om-node/blob/5777b6c/lib/schema/schema.ts#L98) - -## Methods - -### fieldByName - -▸ **fieldByName**(`name`): ``null`` \| [`Field`](Field.md) - -Gets a single [Field](Field.md) defined by this Schema. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `name` | `string` | The name of the [Field](Field.md) in this Schema. | - -#### Returns - -``null`` \| [`Field`](Field.md) - -The [Field](Field.md), or null of not found. - -#### Defined in - -[lib/schema/schema.ts:78](https://github.com/redis/redis-om-node/blob/5777b6c/lib/schema/schema.ts#L78) - -___ - -### generateId - -▸ **generateId**(): `Promise`<`string`\> - -Generates a unique string using the configured [IdStrategy](../README.md#idstrategy). - -#### Returns - -`Promise`<`string`\> - -The generated id. - -#### Defined in - -[lib/schema/schema.ts:111](https://github.com/redis/redis-om-node/blob/5777b6c/lib/schema/schema.ts#L111) diff --git a/docs/classes/Search.md b/docs/classes/Search.md deleted file mode 100644 index edf2162..0000000 --- a/docs/classes/Search.md +++ /dev/null @@ -1,1199 +0,0 @@ -[redis-om](../README.md) / Search - -# Class: Search - -Entry point to fluent search. This is the default Redis OM experience. -Requires that RediSearch (and optionally RedisJSON) be installed. - -**`Template`** - -The type of [Entity](../README.md#entity) being sought. - -## Hierarchy - -- [`AbstractSearch`](AbstractSearch.md) - - ↳ **`Search`** - -## Table of contents - -### Accessors - -- [return](Search.md#return) - -### Methods - -- [all](Search.md#all) -- [allIds](Search.md#allids) -- [allKeys](Search.md#allkeys) -- [and](Search.md#and) -- [count](Search.md#count) -- [first](Search.md#first) -- [firstId](Search.md#firstid) -- [firstKey](Search.md#firstkey) -- [max](Search.md#max) -- [maxId](Search.md#maxid) -- [maxKey](Search.md#maxkey) -- [min](Search.md#min) -- [minId](Search.md#minid) -- [minKey](Search.md#minkey) -- [or](Search.md#or) -- [page](Search.md#page) -- [pageOfIds](Search.md#pageofids) -- [pageOfKeys](Search.md#pageofkeys) -- [returnAll](Search.md#returnall) -- [returnAllIds](Search.md#returnallids) -- [returnAllKeys](Search.md#returnallkeys) -- [returnCount](Search.md#returncount) -- [returnFirst](Search.md#returnfirst) -- [returnFirstId](Search.md#returnfirstid) -- [returnFirstKey](Search.md#returnfirstkey) -- [returnMax](Search.md#returnmax) -- [returnMaxId](Search.md#returnmaxid) -- [returnMaxKey](Search.md#returnmaxkey) -- [returnMin](Search.md#returnmin) -- [returnMinId](Search.md#returnminid) -- [returnMinKey](Search.md#returnminkey) -- [returnPage](Search.md#returnpage) -- [returnPageOfIds](Search.md#returnpageofids) -- [returnPageOfKeys](Search.md#returnpageofkeys) -- [sortAsc](Search.md#sortasc) -- [sortAscending](Search.md#sortascending) -- [sortBy](Search.md#sortby) -- [sortDesc](Search.md#sortdesc) -- [sortDescending](Search.md#sortdescending) -- [where](Search.md#where) - -## Accessors - -### return - -• `get` **return**(): [`AbstractSearch`](AbstractSearch.md) - -Returns the current instance. Syntactic sugar to make your code more fluent. - -#### Returns - -[`AbstractSearch`](AbstractSearch.md) - -this - -#### Inherited from - -AbstractSearch.return - -#### Defined in - -[lib/search/search.ts:308](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L308) - -## Methods - -### all - -▸ **all**(`options?`): `Promise`<[`Entity`](../README.md#entity)[]\> - -Returns all the [Entities](../README.md#entity) that match this query. This method -makes multiple calls to Redis until all the [Entities](../README.md#entity) are returned. -You can specify the batch size by setting the `pageSize` property on the -options: - -```typescript -const entities = await repository.search().returnAll({ pageSize: 100 }) -``` - -#### Parameters - -| Name | Type | Default value | Description | -| :------ | :------ | :------ | :------ | -| `options` | `Object` | `undefined` | Options for the call. | -| `options.pageSize` | `number` | `10` | Number of [Entities](../README.md#entity) returned per batch. | - -#### Returns - -`Promise`<[`Entity`](../README.md#entity)[]\> - -An array of [Entities](../README.md#entity) matching the query. - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[all](AbstractSearch.md#all) - -#### Defined in - -[lib/search/search.ts:264](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L264) - -___ - -### allIds - -▸ **allIds**(`options?`): `Promise`<`string`[]\> - -Returns all the entity IDs that match this query. This method -makes multiple calls to Redis until all the entity IDs are returned. -You can specify the batch size by setting the `pageSize` property on the -options: - -```typescript -const keys = await repository.search().returnAllIds({ pageSize: 100 }) -``` - -#### Parameters - -| Name | Type | Default value | Description | -| :------ | :------ | :------ | :------ | -| `options` | `Object` | `undefined` | Options for the call. | -| `options.pageSize` | `number` | `10` | Number of entity IDs returned per batch. | - -#### Returns - -`Promise`<`string`[]\> - -An array of entity IDs matching the query. - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[allIds](AbstractSearch.md#allids) - -#### Defined in - -[lib/search/search.ts:282](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L282) - -___ - -### allKeys - -▸ **allKeys**(`options?`): `Promise`<`string`[]\> - -Returns all the key names in Redis that match this query. This method -makes multiple calls to Redis until all the key names are returned. -You can specify the batch size by setting the `pageSize` property on the -options: - -```typescript -const keys = await repository.search().returnAllKeys({ pageSize: 100 }) -``` - -#### Parameters - -| Name | Type | Default value | Description | -| :------ | :------ | :------ | :------ | -| `options` | `Object` | `undefined` | Options for the call. | -| `options.pageSize` | `number` | `10` | Number of key names returned per batch. | - -#### Returns - -`Promise`<`string`[]\> - -An array of key names matching the query. - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[allKeys](AbstractSearch.md#allkeys) - -#### Defined in - -[lib/search/search.ts:300](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L300) - -___ - -### and - -▸ **and**(`field`): [`WhereField`](WhereField.md) - -Sets up a query matching a particular field as a logical AND. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `field` | `string` | The field to filter on. | - -#### Returns - -[`WhereField`](WhereField.md) - -A subclass of [WhereField](WhereField.md) matching the type of the field. - -#### Defined in - -[lib/search/search.ts:530](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L530) - -▸ **and**(`subSearchFn`): [`Search`](Search.md) - -Sets up a nested search as a logical AND. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `subSearchFn` | [`SubSearchFunction`](../README.md#subsearchfunction) | A function that takes a [Search](Search.md) and returns another [Search](Search.md). | - -#### Returns - -[`Search`](Search.md) - -`this`. - -#### Defined in - -[lib/search/search.ts:537](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L537) - -___ - -### count - -▸ **count**(): `Promise`<`number`\> - -Returns the number of [Entities](../README.md#entity) that match this query. - -#### Returns - -`Promise`<`number`\> - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[count](AbstractSearch.md#count) - -#### Defined in - -[lib/search/search.ts:188](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L188) - -___ - -### first - -▸ **first**(): `Promise`<``null`` \| [`Entity`](../README.md#entity)\> - -Returns the first [Entity](../README.md#entity) that matches this query. - -#### Returns - -`Promise`<``null`` \| [`Entity`](../README.md#entity)\> - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[first](AbstractSearch.md#first) - -#### Defined in - -[lib/search/search.ts:229](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L229) - -___ - -### firstId - -▸ **firstId**(): `Promise`<``null`` \| `string`\> - -Returns the first entity ID that matches this query. - -#### Returns - -`Promise`<``null`` \| `string`\> - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[firstId](AbstractSearch.md#firstid) - -#### Defined in - -[lib/search/search.ts:237](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L237) - -___ - -### firstKey - -▸ **firstKey**(): `Promise`<``null`` \| `string`\> - -Returns the first key name that matches this query. - -#### Returns - -`Promise`<``null`` \| `string`\> - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[firstKey](AbstractSearch.md#firstkey) - -#### Defined in - -[lib/search/search.ts:245](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L245) - -___ - -### max - -▸ **max**(`field`): `Promise`<``null`` \| [`Entity`](../README.md#entity)\> - -Finds the [Entity](../README.md#entity) with the maximal value for a field. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `field` | `string` | The field with the maximal value. | - -#### Returns - -`Promise`<``null`` \| [`Entity`](../README.md#entity)\> - -The entity ID [Entity](../README.md#entity) with the maximal value - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[max](AbstractSearch.md#max) - -#### Defined in - -[lib/search/search.ts:162](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L162) - -___ - -### maxId - -▸ **maxId**(`field`): `Promise`<``null`` \| `string`\> - -Finds the entity ID with the maximal value for a field. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `field` | `string` | The field with the maximal value. | - -#### Returns - -`Promise`<``null`` \| `string`\> - -The entity ID with the maximal value - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[maxId](AbstractSearch.md#maxid) - -#### Defined in - -[lib/search/search.ts:171](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L171) - -___ - -### maxKey - -▸ **maxKey**(`field`): `Promise`<``null`` \| `string`\> - -Finds the key name in Redis with the maximal value for a field. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `field` | `string` | The field with the maximal value. | - -#### Returns - -`Promise`<``null`` \| `string`\> - -The key name with the maximal value - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[maxKey](AbstractSearch.md#maxkey) - -#### Defined in - -[lib/search/search.ts:180](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L180) - -___ - -### min - -▸ **min**(`field`): `Promise`<``null`` \| [`Entity`](../README.md#entity)\> - -Finds the [Entity](../README.md#entity) with the minimal value for a field. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `field` | `string` | The field with the minimal value. | - -#### Returns - -`Promise`<``null`` \| [`Entity`](../README.md#entity)\> - -The [Entity](../README.md#entity) with the minimal value - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[min](AbstractSearch.md#min) - -#### Defined in - -[lib/search/search.ts:135](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L135) - -___ - -### minId - -▸ **minId**(`field`): `Promise`<``null`` \| `string`\> - -Finds the entity ID with the minimal value for a field. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `field` | `string` | The field with the minimal value. | - -#### Returns - -`Promise`<``null`` \| `string`\> - -The entity ID with the minimal value - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[minId](AbstractSearch.md#minid) - -#### Defined in - -[lib/search/search.ts:144](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L144) - -___ - -### minKey - -▸ **minKey**(`field`): `Promise`<``null`` \| `string`\> - -Finds the key name in Redis with the minimal value for a field. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `field` | `string` | The field with the minimal value. | - -#### Returns - -`Promise`<``null`` \| `string`\> - -The key name with the minimal value - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[minKey](AbstractSearch.md#minkey) - -#### Defined in - -[lib/search/search.ts:153](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L153) - -___ - -### or - -▸ **or**(`field`): [`WhereField`](WhereField.md) - -Sets up a query matching a particular field as a logical OR. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `field` | `string` | The field to filter on. | - -#### Returns - -[`WhereField`](WhereField.md) - -A subclass of [WhereField](WhereField.md) matching the type of the field. - -#### Defined in - -[lib/search/search.ts:547](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L547) - -▸ **or**(`subSearchFn`): [`Search`](Search.md) - -Sets up a nested search as a logical OR. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `subSearchFn` | [`SubSearchFunction`](../README.md#subsearchfunction) | A function that takes a [Search](Search.md) and returns another [Search](Search.md). | - -#### Returns - -[`Search`](Search.md) - -`this`. - -#### Defined in - -[lib/search/search.ts:554](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L554) - -___ - -### page - -▸ **page**(`offset`, `count`): `Promise`<[`Entity`](../README.md#entity)[]\> - -Returns a page of [Entities](../README.md#entity) that match this query. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `offset` | `number` | The offset for where to start returning [Entities](../README.md#entity). | -| `count` | `number` | The number of [Entities](../README.md#entity) to return. | - -#### Returns - -`Promise`<[`Entity`](../README.md#entity)[]\> - -An array of [Entities](../README.md#entity) matching the query. - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[page](AbstractSearch.md#page) - -#### Defined in - -[lib/search/search.ts:199](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L199) - -___ - -### pageOfIds - -▸ **pageOfIds**(`offset`, `count`): `Promise`<`string`[]\> - -Returns a page of entity IDs that match this query. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `offset` | `number` | The offset for where to start returning entity IDs. | -| `count` | `number` | The number of entity IDs to return. | - -#### Returns - -`Promise`<`string`[]\> - -An array of strings matching the query. - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[pageOfIds](AbstractSearch.md#pageofids) - -#### Defined in - -[lib/search/search.ts:210](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L210) - -___ - -### pageOfKeys - -▸ **pageOfKeys**(`offset`, `count`): `Promise`<`string`[]\> - -Returns a page of key names in Redis that match this query. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `offset` | `number` | The offset for where to start returning key names. | -| `count` | `number` | The number of key names to return. | - -#### Returns - -`Promise`<`string`[]\> - -An array of strings matching the query. - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[pageOfKeys](AbstractSearch.md#pageofkeys) - -#### Defined in - -[lib/search/search.ts:221](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L221) - -___ - -### returnAll - -▸ **returnAll**(`options?`): `Promise`<[`Entity`](../README.md#entity)[]\> - -Alias for [all](Search.md#all). - -#### Parameters - -| Name | Type | Default value | -| :------ | :------ | :------ | -| `options` | `Object` | `undefined` | -| `options.pageSize` | `number` | `10` | - -#### Returns - -`Promise`<[`Entity`](../README.md#entity)[]\> - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[returnAll](AbstractSearch.md#returnall) - -#### Defined in - -[lib/search/search.ts:406](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L406) - -___ - -### returnAllIds - -▸ **returnAllIds**(`options?`): `Promise`<`string`[]\> - -Alias for [allIds](Search.md#allids). - -#### Parameters - -| Name | Type | Default value | -| :------ | :------ | :------ | -| `options` | `Object` | `undefined` | -| `options.pageSize` | `number` | `10` | - -#### Returns - -`Promise`<`string`[]\> - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[returnAllIds](AbstractSearch.md#returnallids) - -#### Defined in - -[lib/search/search.ts:413](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L413) - -___ - -### returnAllKeys - -▸ **returnAllKeys**(`options?`): `Promise`<`string`[]\> - -Alias for [allKeys](Search.md#allkeys). - -#### Parameters - -| Name | Type | Default value | -| :------ | :------ | :------ | -| `options` | `Object` | `undefined` | -| `options.pageSize` | `number` | `10` | - -#### Returns - -`Promise`<`string`[]\> - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[returnAllKeys](AbstractSearch.md#returnallkeys) - -#### Defined in - -[lib/search/search.ts:420](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L420) - -___ - -### returnCount - -▸ **returnCount**(): `Promise`<`number`\> - -Alias for [count](Search.md#count). - -#### Returns - -`Promise`<`number`\> - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[returnCount](AbstractSearch.md#returncount) - -#### Defined in - -[lib/search/search.ts:357](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L357) - -___ - -### returnFirst - -▸ **returnFirst**(): `Promise`<``null`` \| [`Entity`](../README.md#entity)\> - -Alias for [first](Search.md#first). - -#### Returns - -`Promise`<``null`` \| [`Entity`](../README.md#entity)\> - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[returnFirst](AbstractSearch.md#returnfirst) - -#### Defined in - -[lib/search/search.ts:385](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L385) - -___ - -### returnFirstId - -▸ **returnFirstId**(): `Promise`<``null`` \| `string`\> - -Alias for [firstId](Search.md#firstid). - -#### Returns - -`Promise`<``null`` \| `string`\> - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[returnFirstId](AbstractSearch.md#returnfirstid) - -#### Defined in - -[lib/search/search.ts:392](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L392) - -___ - -### returnFirstKey - -▸ **returnFirstKey**(): `Promise`<``null`` \| `string`\> - -Alias for [firstKey](Search.md#firstkey). - -#### Returns - -`Promise`<``null`` \| `string`\> - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[returnFirstKey](AbstractSearch.md#returnfirstkey) - -#### Defined in - -[lib/search/search.ts:399](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L399) - -___ - -### returnMax - -▸ **returnMax**(`field`): `Promise`<``null`` \| [`Entity`](../README.md#entity)\> - -Alias for [max](Search.md#max). - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `field` | `string` | - -#### Returns - -`Promise`<``null`` \| [`Entity`](../README.md#entity)\> - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[returnMax](AbstractSearch.md#returnmax) - -#### Defined in - -[lib/search/search.ts:336](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L336) - -___ - -### returnMaxId - -▸ **returnMaxId**(`field`): `Promise`<``null`` \| `string`\> - -Alias for [maxId](Search.md#maxid). - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `field` | `string` | - -#### Returns - -`Promise`<``null`` \| `string`\> - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[returnMaxId](AbstractSearch.md#returnmaxid) - -#### Defined in - -[lib/search/search.ts:343](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L343) - -___ - -### returnMaxKey - -▸ **returnMaxKey**(`field`): `Promise`<``null`` \| `string`\> - -Alias for [maxKey](Search.md#maxkey). - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `field` | `string` | - -#### Returns - -`Promise`<``null`` \| `string`\> - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[returnMaxKey](AbstractSearch.md#returnmaxkey) - -#### Defined in - -[lib/search/search.ts:350](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L350) - -___ - -### returnMin - -▸ **returnMin**(`field`): `Promise`<``null`` \| [`Entity`](../README.md#entity)\> - -Alias for [min](Search.md#min). - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `field` | `string` | - -#### Returns - -`Promise`<``null`` \| [`Entity`](../README.md#entity)\> - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[returnMin](AbstractSearch.md#returnmin) - -#### Defined in - -[lib/search/search.ts:315](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L315) - -___ - -### returnMinId - -▸ **returnMinId**(`field`): `Promise`<``null`` \| `string`\> - -Alias for [minId](Search.md#minid). - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `field` | `string` | - -#### Returns - -`Promise`<``null`` \| `string`\> - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[returnMinId](AbstractSearch.md#returnminid) - -#### Defined in - -[lib/search/search.ts:322](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L322) - -___ - -### returnMinKey - -▸ **returnMinKey**(`field`): `Promise`<``null`` \| `string`\> - -Alias for [minKey](Search.md#minkey). - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `field` | `string` | - -#### Returns - -`Promise`<``null`` \| `string`\> - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[returnMinKey](AbstractSearch.md#returnminkey) - -#### Defined in - -[lib/search/search.ts:329](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L329) - -___ - -### returnPage - -▸ **returnPage**(`offset`, `count`): `Promise`<[`Entity`](../README.md#entity)[]\> - -Alias for [page](Search.md#page). - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `offset` | `number` | -| `count` | `number` | - -#### Returns - -`Promise`<[`Entity`](../README.md#entity)[]\> - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[returnPage](AbstractSearch.md#returnpage) - -#### Defined in - -[lib/search/search.ts:364](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L364) - -___ - -### returnPageOfIds - -▸ **returnPageOfIds**(`offset`, `count`): `Promise`<`string`[]\> - -Alias for [pageOfIds](Search.md#pageofids). - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `offset` | `number` | -| `count` | `number` | - -#### Returns - -`Promise`<`string`[]\> - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[returnPageOfIds](AbstractSearch.md#returnpageofids) - -#### Defined in - -[lib/search/search.ts:371](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L371) - -___ - -### returnPageOfKeys - -▸ **returnPageOfKeys**(`offset`, `count`): `Promise`<`string`[]\> - -Alias for [pageOfKeys](Search.md#pageofkeys). - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `offset` | `number` | -| `count` | `number` | - -#### Returns - -`Promise`<`string`[]\> - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[returnPageOfKeys](AbstractSearch.md#returnpageofkeys) - -#### Defined in - -[lib/search/search.ts:378](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L378) - -___ - -### sortAsc - -▸ **sortAsc**(`field`): [`AbstractSearch`](AbstractSearch.md) - -Alias for [sortAscending](Search.md#sortascending). - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `field` | `string` | - -#### Returns - -[`AbstractSearch`](AbstractSearch.md) - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[sortAsc](AbstractSearch.md#sortasc) - -#### Defined in - -[lib/search/search.ts:86](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L86) - -___ - -### sortAscending - -▸ **sortAscending**(`field`): [`AbstractSearch`](AbstractSearch.md) - -Applies an ascending sort to the query. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `field` | `string` | The field to sort by. | - -#### Returns - -[`AbstractSearch`](AbstractSearch.md) - -this - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[sortAscending](AbstractSearch.md#sortascending) - -#### Defined in - -[lib/search/search.ts:63](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L63) - -___ - -### sortBy - -▸ **sortBy**(`fieldName`, `order?`): [`AbstractSearch`](AbstractSearch.md) - -Applies sorting for the query. - -#### Parameters - -| Name | Type | Default value | Description | -| :------ | :------ | :------ | :------ | -| `fieldName` | `string` | `undefined` | - | -| `order` | ``"ASC"`` \| ``"DESC"`` | `'ASC'` | The order of returned [Entities](../README.md#entity) Defaults to `ASC` (ascending) if not specified | - -#### Returns - -[`AbstractSearch`](AbstractSearch.md) - -this - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[sortBy](AbstractSearch.md#sortby) - -#### Defined in - -[lib/search/search.ts:96](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L96) - -___ - -### sortDesc - -▸ **sortDesc**(`field`): [`AbstractSearch`](AbstractSearch.md) - -Alias for [sortDescending](Search.md#sortdescending). - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `field` | `string` | - -#### Returns - -[`AbstractSearch`](AbstractSearch.md) - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[sortDesc](AbstractSearch.md#sortdesc) - -#### Defined in - -[lib/search/search.ts:70](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L70) - -___ - -### sortDescending - -▸ **sortDescending**(`field`): [`AbstractSearch`](AbstractSearch.md) - -Applies a descending sort to the query. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `field` | `string` | The field to sort by. | - -#### Returns - -[`AbstractSearch`](AbstractSearch.md) - -this - -#### Inherited from - -[AbstractSearch](AbstractSearch.md).[sortDescending](AbstractSearch.md#sortdescending) - -#### Defined in - -[lib/search/search.ts:79](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L79) - -___ - -### where - -▸ **where**(`field`): [`WhereField`](WhereField.md) - -Sets up a query matching a particular field. If there are multiple calls -to [where](Search.md#where), they are treated logically as AND. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `field` | `string` | The field to filter on. | - -#### Returns - -[`WhereField`](WhereField.md) - -A subclass of [WhereField](WhereField.md) matching the type of the field. - -#### Defined in - -[lib/search/search.ts:512](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L512) - -▸ **where**(`subSearchFn`): [`Search`](Search.md) - -Sets up a nested search. If there are multiple calls to [where](Search.md#where), -they are treated logically as AND. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `subSearchFn` | [`SubSearchFunction`](../README.md#subsearchfunction) | A function that takes a [Search](Search.md) and returns another [Search](Search.md). | - -#### Returns - -[`Search`](Search.md) - -`this`. - -#### Defined in - -[lib/search/search.ts:520](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/search.ts#L520) diff --git a/docs/classes/SearchError.md b/docs/classes/SearchError.md deleted file mode 100644 index c60d5b5..0000000 --- a/docs/classes/SearchError.md +++ /dev/null @@ -1,201 +0,0 @@ -[redis-om](../README.md) / SearchError - -# Class: SearchError - -## Hierarchy - -- [`RedisOmError`](RedisOmError.md) - - ↳ **`SearchError`** - - ↳↳ [`SemanticSearchError`](SemanticSearchError.md) - - ↳↳ [`FieldNotInSchema`](FieldNotInSchema.md) - -## Table of contents - -### Constructors - -- [constructor](SearchError.md#constructor) - -### Properties - -- [cause](SearchError.md#cause) -- [message](SearchError.md#message) -- [name](SearchError.md#name) -- [stack](SearchError.md#stack) -- [prepareStackTrace](SearchError.md#preparestacktrace) -- [stackTraceLimit](SearchError.md#stacktracelimit) - -### Methods - -- [captureStackTrace](SearchError.md#capturestacktrace) - -## Constructors - -### constructor - -• **new SearchError**(`message?`) - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `message?` | `string` | - -#### Inherited from - -[RedisOmError](RedisOmError.md).[constructor](RedisOmError.md#constructor) - -#### Defined in - -node_modules/typescript/lib/lib.es5.d.ts:1059 - -• **new SearchError**(`message?`, `options?`) - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `message?` | `string` | -| `options?` | `ErrorOptions` | - -#### Inherited from - -[RedisOmError](RedisOmError.md).[constructor](RedisOmError.md#constructor) - -#### Defined in - -node_modules/typescript/lib/lib.es2022.error.d.ts:30 - -## Properties - -### cause - -• `Optional` **cause**: `unknown` - -#### Inherited from - -[RedisOmError](RedisOmError.md).[cause](RedisOmError.md#cause) - -#### Defined in - -node_modules/typescript/lib/lib.es2022.error.d.ts:26 - -___ - -### message - -• **message**: `string` - -#### Inherited from - -[RedisOmError](RedisOmError.md).[message](RedisOmError.md#message) - -#### Defined in - -node_modules/typescript/lib/lib.es5.d.ts:1054 - -___ - -### name - -• **name**: `string` - -#### Inherited from - -[RedisOmError](RedisOmError.md).[name](RedisOmError.md#name) - -#### Defined in - -node_modules/typescript/lib/lib.es5.d.ts:1053 - -___ - -### stack - -• `Optional` **stack**: `string` - -#### Inherited from - -[RedisOmError](RedisOmError.md).[stack](RedisOmError.md#stack) - -#### Defined in - -node_modules/typescript/lib/lib.es5.d.ts:1055 - -___ - -### prepareStackTrace - -▪ `Static` `Optional` **prepareStackTrace**: (`err`: `Error`, `stackTraces`: `CallSite`[]) => `any` - -#### Type declaration - -▸ (`err`, `stackTraces`): `any` - -Optional override for formatting stack traces - -**`See`** - -https://v8.dev/docs/stack-trace-api#customizing-stack-traces - -##### Parameters - -| Name | Type | -| :------ | :------ | -| `err` | `Error` | -| `stackTraces` | `CallSite`[] | - -##### Returns - -`any` - -#### Inherited from - -[RedisOmError](RedisOmError.md).[prepareStackTrace](RedisOmError.md#preparestacktrace) - -#### Defined in - -node_modules/@types/node/globals.d.ts:11 - -___ - -### stackTraceLimit - -▪ `Static` **stackTraceLimit**: `number` - -#### Inherited from - -[RedisOmError](RedisOmError.md).[stackTraceLimit](RedisOmError.md#stacktracelimit) - -#### Defined in - -node_modules/@types/node/globals.d.ts:13 - -## Methods - -### captureStackTrace - -▸ `Static` **captureStackTrace**(`targetObject`, `constructorOpt?`): `void` - -Create .stack property on a target object - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `targetObject` | `object` | -| `constructorOpt?` | `Function` | - -#### Returns - -`void` - -#### Inherited from - -[RedisOmError](RedisOmError.md).[captureStackTrace](RedisOmError.md#capturestacktrace) - -#### Defined in - -node_modules/@types/node/globals.d.ts:4 diff --git a/docs/classes/SemanticSearchError.md b/docs/classes/SemanticSearchError.md deleted file mode 100644 index f21e120..0000000 --- a/docs/classes/SemanticSearchError.md +++ /dev/null @@ -1,197 +0,0 @@ -[redis-om](../README.md) / SemanticSearchError - -# Class: SemanticSearchError - -## Hierarchy - -- [`SearchError`](SearchError.md) - - ↳ **`SemanticSearchError`** - -## Table of contents - -### Constructors - -- [constructor](SemanticSearchError.md#constructor) - -### Properties - -- [cause](SemanticSearchError.md#cause) -- [message](SemanticSearchError.md#message) -- [name](SemanticSearchError.md#name) -- [stack](SemanticSearchError.md#stack) -- [prepareStackTrace](SemanticSearchError.md#preparestacktrace) -- [stackTraceLimit](SemanticSearchError.md#stacktracelimit) - -### Methods - -- [captureStackTrace](SemanticSearchError.md#capturestacktrace) - -## Constructors - -### constructor - -• **new SemanticSearchError**(`message?`) - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `message?` | `string` | - -#### Inherited from - -[SearchError](SearchError.md).[constructor](SearchError.md#constructor) - -#### Defined in - -node_modules/typescript/lib/lib.es5.d.ts:1059 - -• **new SemanticSearchError**(`message?`, `options?`) - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `message?` | `string` | -| `options?` | `ErrorOptions` | - -#### Inherited from - -[SearchError](SearchError.md).[constructor](SearchError.md#constructor) - -#### Defined in - -node_modules/typescript/lib/lib.es2022.error.d.ts:30 - -## Properties - -### cause - -• `Optional` **cause**: `unknown` - -#### Inherited from - -[SearchError](SearchError.md).[cause](SearchError.md#cause) - -#### Defined in - -node_modules/typescript/lib/lib.es2022.error.d.ts:26 - -___ - -### message - -• **message**: `string` - -#### Inherited from - -[SearchError](SearchError.md).[message](SearchError.md#message) - -#### Defined in - -node_modules/typescript/lib/lib.es5.d.ts:1054 - -___ - -### name - -• **name**: `string` - -#### Inherited from - -[SearchError](SearchError.md).[name](SearchError.md#name) - -#### Defined in - -node_modules/typescript/lib/lib.es5.d.ts:1053 - -___ - -### stack - -• `Optional` **stack**: `string` - -#### Inherited from - -[SearchError](SearchError.md).[stack](SearchError.md#stack) - -#### Defined in - -node_modules/typescript/lib/lib.es5.d.ts:1055 - -___ - -### prepareStackTrace - -▪ `Static` `Optional` **prepareStackTrace**: (`err`: `Error`, `stackTraces`: `CallSite`[]) => `any` - -#### Type declaration - -▸ (`err`, `stackTraces`): `any` - -Optional override for formatting stack traces - -**`See`** - -https://v8.dev/docs/stack-trace-api#customizing-stack-traces - -##### Parameters - -| Name | Type | -| :------ | :------ | -| `err` | `Error` | -| `stackTraces` | `CallSite`[] | - -##### Returns - -`any` - -#### Inherited from - -[SearchError](SearchError.md).[prepareStackTrace](SearchError.md#preparestacktrace) - -#### Defined in - -node_modules/@types/node/globals.d.ts:11 - -___ - -### stackTraceLimit - -▪ `Static` **stackTraceLimit**: `number` - -#### Inherited from - -[SearchError](SearchError.md).[stackTraceLimit](SearchError.md#stacktracelimit) - -#### Defined in - -node_modules/@types/node/globals.d.ts:13 - -## Methods - -### captureStackTrace - -▸ `Static` **captureStackTrace**(`targetObject`, `constructorOpt?`): `void` - -Create .stack property on a target object - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `targetObject` | `object` | -| `constructorOpt?` | `Function` | - -#### Returns - -`void` - -#### Inherited from - -[SearchError](SearchError.md).[captureStackTrace](SearchError.md#capturestacktrace) - -#### Defined in - -node_modules/@types/node/globals.d.ts:4 diff --git a/docs/classes/Where.md b/docs/classes/Where.md deleted file mode 100644 index 273efe0..0000000 --- a/docs/classes/Where.md +++ /dev/null @@ -1,43 +0,0 @@ -[redis-om](../README.md) / Where - -# Class: Where - -Abstract base class used extensively with [Search](Search.md). - -## Hierarchy - -- **`Where`** - - ↳ [`WhereField`](WhereField.md) - -## Table of contents - -### Constructors - -- [constructor](Where.md#constructor) - -### Methods - -- [toString](Where.md#tostring) - -## Constructors - -### constructor - -• **new Where**() - -## Methods - -### toString - -▸ `Abstract` **toString**(): `string` - -Converts this [Where](Where.md) into a portion of a RediSearch query. - -#### Returns - -`string` - -#### Defined in - -[lib/search/where.ts:8](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/where.ts#L8) diff --git a/docs/classes/WhereField.md b/docs/classes/WhereField.md deleted file mode 100644 index eef49b6..0000000 --- a/docs/classes/WhereField.md +++ /dev/null @@ -1,909 +0,0 @@ -[redis-om](../README.md) / WhereField - -# Class: WhereField - -Abstract base class that all fields you want to filter -with extend. When you call [where](Search.md#where), a -subclass of this is returned. - -## Hierarchy - -- [`Where`](Where.md) - - ↳ **`WhereField`** - -## Table of contents - -### Properties - -- [exact](WhereField.md#exact) -- [exactly](WhereField.md#exactly) - -### Accessors - -- [does](WhereField.md#does) -- [is](WhereField.md#is) -- [not](WhereField.md#not) - -### Methods - -- [after](WhereField.md#after) -- [before](WhereField.md#before) -- [between](WhereField.md#between) -- [contain](WhereField.md#contain) -- [containOneOf](WhereField.md#containoneof) -- [contains](WhereField.md#contains) -- [containsOneOf](WhereField.md#containsoneof) -- [eq](WhereField.md#eq) -- [equal](WhereField.md#equal) -- [equalTo](WhereField.md#equalto) -- [equals](WhereField.md#equals) -- [false](WhereField.md#false) -- [greaterThan](WhereField.md#greaterthan) -- [greaterThanOrEqualTo](WhereField.md#greaterthanorequalto) -- [gt](WhereField.md#gt) -- [gte](WhereField.md#gte) -- [inCircle](WhereField.md#incircle) -- [inRadius](WhereField.md#inradius) -- [lessThan](WhereField.md#lessthan) -- [lessThanOrEqualTo](WhereField.md#lessthanorequalto) -- [lt](WhereField.md#lt) -- [lte](WhereField.md#lte) -- [match](WhereField.md#match) -- [matchExact](WhereField.md#matchexact) -- [matchExactly](WhereField.md#matchexactly) -- [matches](WhereField.md#matches) -- [matchesExactly](WhereField.md#matchesexactly) -- [on](WhereField.md#on) -- [onOrAfter](WhereField.md#onorafter) -- [onOrBefore](WhereField.md#onorbefore) -- [toString](WhereField.md#tostring) -- [true](WhereField.md#true) - -## Properties - -### exact - -• `Readonly` **exact**: [`WhereField`](WhereField.md) - -Makes a call to [match](WhereField.md#match) a [matchExact](WhereField.md#matchexact) call. Calling -this multiple times will have no effect. - -#### Defined in - -[lib/search/where-field.ts:92](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/where-field.ts#L92) - -___ - -### exactly - -• `Readonly` **exactly**: [`WhereField`](WhereField.md) - -Makes a call to [match](WhereField.md#match) a [matchExact](WhereField.md#matchexact) call. Calling -this multiple times will have no effect. - -#### Defined in - -[lib/search/where-field.ts:99](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/where-field.ts#L99) - -## Accessors - -### does - -• `get` **does**(): `this` - -Returns the current instance. Syntactic sugar to make your code more fluent. - -#### Returns - -`this` - -this - -#### Defined in - -[lib/search/where-field.ts:289](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/where-field.ts#L289) - -___ - -### is - -• `get` **is**(): `this` - -Returns the current instance. Syntactic sugar to make your code more fluent. - -#### Returns - -`this` - -this - -#### Defined in - -[lib/search/where-field.ts:281](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/where-field.ts#L281) - -___ - -### not - -• `get` **not**(): `this` - -Negates the query on the field, cause it to match when the condition is -*not* met. Calling this multiple times will negate the negation. - -#### Returns - -`this` - -this - -#### Defined in - -[lib/search/where-field.ts:298](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/where-field.ts#L298) - -## Methods - -### after - -▸ **after**(`value`): [`Search`](Search.md) - -Add a search that matches all datetimes *after* the provided UTC datetime to the query. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `value` | `string` \| `number` \| `Date` | The datetime to compare against. | - -#### Returns - -[`Search`](Search.md) - -The [Search](Search.md) that was called to create this [WhereField](WhereField.md). - -#### Defined in - -[lib/search/where-field.ts:240](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/where-field.ts#L240) - -___ - -### before - -▸ **before**(`value`): [`Search`](Search.md) - -Add a search that matches all datetimes *before* the provided UTC datetime to the query. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `value` | `string` \| `number` \| `Date` | The datetime to compare against. | - -#### Returns - -[`Search`](Search.md) - -The [Search](Search.md) that was called to create this [WhereField](WhereField.md). - -#### Defined in - -[lib/search/where-field.ts:233](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/where-field.ts#L233) - -___ - -### between - -▸ **between**(`lower`, `upper`): [`Search`](Search.md) - -Adds an inclusive range comparison against a field to the search query. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `lower` | `string` \| `number` \| `Date` | The lower bound of the range. | -| `upper` | `string` \| `number` \| `Date` | The upper bound of the range. | - -#### Returns - -[`Search`](Search.md) - -The [Search](Search.md) that was called to create this [WhereField](WhereField.md). - -#### Defined in - -[lib/search/where-field.ts:175](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/where-field.ts#L175) - -___ - -### contain - -▸ **contain**(`value`): [`Search`](Search.md) - -Adds a whole-string match for a value within a string array to the search query. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `value` | `string` | The string to be matched. | - -#### Returns - -[`Search`](Search.md) - -The [Search](Search.md) that was called to create this [WhereField](WhereField.md). - -#### Defined in - -[lib/search/where-field.ts:182](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/where-field.ts#L182) - -___ - -### containOneOf - -▸ **containOneOf**(`...value`): [`Search`](Search.md) - -Adds a whole-string match against a string array to the query. If any of the provided -strings in `value` is matched in the array, this matched. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `...value` | `string`[] | An array of strings that you want to match one of. | - -#### Returns - -[`Search`](Search.md) - -The [Search](Search.md) that was called to create this [WhereField](WhereField.md). - -#### Defined in - -[lib/search/where-field.ts:197](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/where-field.ts#L197) - -___ - -### contains - -▸ **contains**(`value`): [`Search`](Search.md) - -Adds a whole-string match for a value within a string array to the search query. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `value` | `string` | The string to be matched. | - -#### Returns - -[`Search`](Search.md) - -The [Search](Search.md) that was called to create this [WhereField](WhereField.md). - -#### Defined in - -[lib/search/where-field.ts:189](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/where-field.ts#L189) - -___ - -### containsOneOf - -▸ **containsOneOf**(`...value`): [`Search`](Search.md) - -Adds a whole-string match against a string array to the query. If any of the provided -strings in `value` is matched in the array, this matched. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `...value` | `string`[] | An array of strings that you want to match one of. | - -#### Returns - -[`Search`](Search.md) - -The [Search](Search.md) that was called to create this [WhereField](WhereField.md). - -#### Defined in - -[lib/search/where-field.ts:205](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/where-field.ts#L205) - -___ - -### eq - -▸ **eq**(`value`): [`Search`](Search.md) - -Adds an equals comparison to the query. - -NOTE: this function is not available for strings where full-text -search is enabled. In that scenario, use `.match`. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `value` | `string` \| `number` \| `boolean` \| `Date` | The value to be compared | - -#### Returns - -[`Search`](Search.md) - -The [Search](Search.md) that was called to create this [WhereField](WhereField.md). - -#### Defined in - -[lib/search/where-field.ts:20](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/where-field.ts#L20) - -___ - -### equal - -▸ **equal**(`value`): [`Search`](Search.md) - -Adds an equals comparison to the query. - -NOTE: this function is not available for strings where full-text -search is enabled. In that scenario, use `.match`. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `value` | `string` \| `number` \| `boolean` \| `Date` | The value to be compared | - -#### Returns - -[`Search`](Search.md) - -The [Search](Search.md) that was called to create this [WhereField](WhereField.md). - -#### Defined in - -[lib/search/where-field.ts:30](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/where-field.ts#L30) - -___ - -### equalTo - -▸ **equalTo**(`value`): [`Search`](Search.md) - -Adds an equals comparison to the query. - -NOTE: this function is not available for strings where full-text -search is enabled. In that scenario, use `.match`. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `value` | `string` \| `number` \| `boolean` \| `Date` | The value to be compared | - -#### Returns - -[`Search`](Search.md) - -The [Search](Search.md) that was called to create this [WhereField](WhereField.md). - -#### Defined in - -[lib/search/where-field.ts:50](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/where-field.ts#L50) - -___ - -### equals - -▸ **equals**(`value`): [`Search`](Search.md) - -Adds an equals comparison to the query. - -NOTE: this function is not available for strings where full-text -search is enabled. In that scenario, use `.match`. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `value` | `string` \| `number` \| `boolean` \| `Date` | The value to be compared | - -#### Returns - -[`Search`](Search.md) - -The [Search](Search.md) that was called to create this [WhereField](WhereField.md). - -#### Defined in - -[lib/search/where-field.ts:40](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/where-field.ts#L40) - -___ - -### false - -▸ **false**(): [`Search`](Search.md) - -Adds a boolean match with a value of `false` to the query. - -#### Returns - -[`Search`](Search.md) - -The [Search](Search.md) that was called to create this [WhereField](WhereField.md). - -#### Defined in - -[lib/search/where-field.ts:111](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/where-field.ts#L111) - -___ - -### greaterThan - -▸ **greaterThan**(`value`): [`Search`](Search.md) - -Adds a greater than comparison against a field to the search query. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `value` | `string` \| `number` \| `Date` | The value to compare against. | - -#### Returns - -[`Search`](Search.md) - -The [Search](Search.md) that was called to create this [WhereField](WhereField.md). - -#### Defined in - -[lib/search/where-field.ts:125](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/where-field.ts#L125) - -___ - -### greaterThanOrEqualTo - -▸ **greaterThanOrEqualTo**(`value`): [`Search`](Search.md) - -Adds a greater than or equal to comparison against a field to the search query. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `value` | `string` \| `number` \| `Date` | The value to compare against. | - -#### Returns - -[`Search`](Search.md) - -The [Search](Search.md) that was called to create this [WhereField](WhereField.md). - -#### Defined in - -[lib/search/where-field.ts:139](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/where-field.ts#L139) - -___ - -### gt - -▸ **gt**(`value`): [`Search`](Search.md) - -Adds a greater than comparison against a field to the search query. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `value` | `string` \| `number` \| `Date` | The value to compare against. | - -#### Returns - -[`Search`](Search.md) - -The [Search](Search.md) that was called to create this [WhereField](WhereField.md). - -#### Defined in - -[lib/search/where-field.ts:118](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/where-field.ts#L118) - -___ - -### gte - -▸ **gte**(`value`): [`Search`](Search.md) - -Adds a greater than or equal to comparison against a field to the search query. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `value` | `string` \| `number` \| `Date` | The value to compare against. | - -#### Returns - -[`Search`](Search.md) - -The [Search](Search.md) that was called to create this [WhereField](WhereField.md). - -#### Defined in - -[lib/search/where-field.ts:132](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/where-field.ts#L132) - -___ - -### inCircle - -▸ **inCircle**(`circleFn`): [`Search`](Search.md) - -Adds a search for points that fall within a defined circle. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `circleFn` | [`CircleFunction`](../README.md#circlefunction) | A function that returns a [Circle](Circle.md) instance defining the search area. | - -#### Returns - -[`Search`](Search.md) - -The [Search](Search.md) that was called to create this [WhereField](WhereField.md). - -#### Defined in - -[lib/search/where-field.ts:212](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/where-field.ts#L212) - -___ - -### inRadius - -▸ **inRadius**(`circleFn`): [`Search`](Search.md) - -Adds a search for points that fall within a defined radius. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `circleFn` | [`CircleFunction`](../README.md#circlefunction) | A function that returns a [Circle](Circle.md) instance defining the search area. | - -#### Returns - -[`Search`](Search.md) - -The [Search](Search.md) that was called to create this [WhereField](WhereField.md). - -#### Defined in - -[lib/search/where-field.ts:219](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/where-field.ts#L219) - -___ - -### lessThan - -▸ **lessThan**(`value`): [`Search`](Search.md) - -Adds a less than comparison against a field to the search query. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `value` | `string` \| `number` \| `Date` | The value to compare against. | - -#### Returns - -[`Search`](Search.md) - -The [Search](Search.md) that was called to create this [WhereField](WhereField.md). - -#### Defined in - -[lib/search/where-field.ts:153](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/where-field.ts#L153) - -___ - -### lessThanOrEqualTo - -▸ **lessThanOrEqualTo**(`value`): [`Search`](Search.md) - -Adds a less than or equal to comparison against a field to the search query. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `value` | `string` \| `number` \| `Date` | The value to compare against. | - -#### Returns - -[`Search`](Search.md) - -The [Search](Search.md) that was called to create this [WhereField](WhereField.md). - -#### Defined in - -[lib/search/where-field.ts:167](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/where-field.ts#L167) - -___ - -### lt - -▸ **lt**(`value`): [`Search`](Search.md) - -Adds a less than comparison against a field to the search query. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `value` | `string` \| `number` \| `Date` | The value to compare against. | - -#### Returns - -[`Search`](Search.md) - -The [Search](Search.md) that was called to create this [WhereField](WhereField.md). - -#### Defined in - -[lib/search/where-field.ts:146](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/where-field.ts#L146) - -___ - -### lte - -▸ **lte**(`value`): [`Search`](Search.md) - -Adds a less than or equal to comparison against a field to the search query. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `value` | `string` \| `number` \| `Date` | The value to compare against. | - -#### Returns - -[`Search`](Search.md) - -The [Search](Search.md) that was called to create this [WhereField](WhereField.md). - -#### Defined in - -[lib/search/where-field.ts:160](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/where-field.ts#L160) - -___ - -### match - -▸ **match**(`value`): [`Search`](Search.md) - -Adds a full-text search comparison to the query. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `value` | `string` \| `number` \| `boolean` | The word or phrase sought. | - -#### Returns - -[`Search`](Search.md) - -The [Search](Search.md) that was called to create this [WhereField](WhereField.md). - -#### Defined in - -[lib/search/where-field.ts:57](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/where-field.ts#L57) - -___ - -### matchExact - -▸ **matchExact**(`value`): [`Search`](Search.md) - -Adds a full-text search comparison to the query that matches an exact word or phrase. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `value` | `string` \| `number` \| `boolean` | The word or phrase sought. | - -#### Returns - -[`Search`](Search.md) - -The [Search](Search.md) that was called to create this [WhereField](WhereField.md). - -#### Defined in - -[lib/search/where-field.ts:71](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/where-field.ts#L71) - -___ - -### matchExactly - -▸ **matchExactly**(`value`): [`Search`](Search.md) - -Adds a full-text search comparison to the query that matches an exact word or phrase. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `value` | `string` \| `number` \| `boolean` | The word or phrase sought. | - -#### Returns - -[`Search`](Search.md) - -The [Search](Search.md) that was called to create this [WhereField](WhereField.md). - -#### Defined in - -[lib/search/where-field.ts:78](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/where-field.ts#L78) - -___ - -### matches - -▸ **matches**(`value`): [`Search`](Search.md) - -Adds a full-text search comparison to the query. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `value` | `string` \| `number` \| `boolean` | The word or phrase sought. | - -#### Returns - -[`Search`](Search.md) - -The [Search](Search.md) that was called to create this [WhereField](WhereField.md). - -#### Defined in - -[lib/search/where-field.ts:64](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/where-field.ts#L64) - -___ - -### matchesExactly - -▸ **matchesExactly**(`value`): [`Search`](Search.md) - -Adds a full-text search comparison to the query that matches an exact word or phrase. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `value` | `string` \| `number` \| `boolean` | The word or phrase sought. | - -#### Returns - -[`Search`](Search.md) - -The [Search](Search.md) that was called to create this [WhereField](WhereField.md). - -#### Defined in - -[lib/search/where-field.ts:85](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/where-field.ts#L85) - -___ - -### on - -▸ **on**(`value`): [`Search`](Search.md) - -Add a search for an exact UTC datetime to the query. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `value` | `string` \| `number` \| `Date` | The datetime to match. | - -#### Returns - -[`Search`](Search.md) - -The [Search](Search.md) that was called to create this [WhereField](WhereField.md). - -#### Defined in - -[lib/search/where-field.ts:226](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/where-field.ts#L226) - -___ - -### onOrAfter - -▸ **onOrAfter**(`value`): [`Search`](Search.md) - -Add a search that matches all datetimes *on or after* the provided UTC datetime to the query. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `value` | `string` \| `number` \| `Date` | The datetime to compare against. | - -#### Returns - -[`Search`](Search.md) - -The [Search](Search.md) that was called to create this [WhereField](WhereField.md). - -#### Defined in - -[lib/search/where-field.ts:254](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/where-field.ts#L254) - -___ - -### onOrBefore - -▸ **onOrBefore**(`value`): [`Search`](Search.md) - -Add a search that matches all datetimes *on or before* the provided UTC datetime to the query. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `value` | `string` \| `number` \| `Date` | The datetime to compare against. | - -#### Returns - -[`Search`](Search.md) - -The [Search](Search.md) that was called to create this [WhereField](WhereField.md). - -#### Defined in - -[lib/search/where-field.ts:247](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/where-field.ts#L247) - -___ - -### toString - -▸ `Abstract` **toString**(): `string` - -Converts this [Where](Where.md) into a portion of a RediSearch query. - -#### Returns - -`string` - -#### Inherited from - -[Where](Where.md).[toString](Where.md#tostring) - -#### Defined in - -[lib/search/where-field.ts:303](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/where-field.ts#L303) - -___ - -### true - -▸ **true**(): [`Search`](Search.md) - -Adds a boolean match with a value of `true` to the query. - -#### Returns - -[`Search`](Search.md) - -The [Search](Search.md) that was called to create this [WhereField](WhereField.md). - -#### Defined in - -[lib/search/where-field.ts:105](https://github.com/redis/redis-om-node/blob/5777b6c/lib/search/where-field.ts#L105) diff --git a/examples/table-module.ts b/examples/table-module.ts new file mode 100644 index 0000000..ba706c1 --- /dev/null +++ b/examples/table-module.ts @@ -0,0 +1,142 @@ +import { PrettyError } from "@infinite-fansub/logger"; + +import { Client, Model } from "../src"; +import { ModelOptions } from "../src/typings"; + +/* +This module allows you to define tables to use with normal redis operations +However it doesn't affect the `Search` functionality since it doesn't have suffix constrains + +Keep in mind that this could be way simpler but it is written this way to try and showcase +the different features and some other possibilities +*/ +class Table> { + readonly #oldSuffix: ModelOptions["suffix"]; + + constructor(name: string, public model: T) { + const { suffix } = model.getOptions; + this.#oldSuffix = suffix; + + if (typeof suffix !== "undefined") { + if (typeof suffix === "function") throw new PrettyError("Using tables is not allowed with dynamic suffixes"); + + model.mutateOptions = { + suffix: `${suffix}:${name}` + } + } else { + model.mutateOptions = { + suffix: name + } + } + } + + /** @internal */ + public _cleanUp(): void { + this.model.mutateOptions = { + suffix: this.#oldSuffix + } + } +} + +type TableFunction = ((table: Table) => Table | Promise>) | Table; + +const client = new Client({ + inject: { + schema: { + methods: { + withTable: async function (name: string, table: TableFunction) { + if (typeof table === "function") (await table(new Table(name, this)))._cleanUp(); + else table._cleanUp(); + } + } + } + } +}); + +// IIFE +(async () => { + + await client.connect().then(() => console.log("Connected!")); + + const simpleSchema = client.schema({ + name: "string", + age: "number" + }) + + const exampleModel = client.model("test", simpleSchema); + await exampleModel.createIndex(); + + await exampleModel.createAndSave({ + $id: 1, + name: "DidaS", + age: 18 + }); + + //#region No Tables + const result1 = await exampleModel.get(1); + + console.log(result1) + /* + Expected Redis log: + + "JSON.GET" "Redis-OM:V1:test:1" + + Expected result: + + JSONDocument { + '$global_prefix': 'Redis-OM', + '$prefix': 'V1', + '$model_name': 'test', + '$suffix': undefined, + '$id': '1', + '$record_id': 'Redis-OM:V1:test:1', + name: 'DidaS', + age: 18 + } + */ + + //#endregion No Tables + + //#region With Tables + + // For some reason typescript is not passing generics around so this is a temporary solution until i figure out how to get generics working + type ExampleTable = Table; + await exampleModel.withTable("table1", async (table: ExampleTable) => { + await table.model.createAndSave({ + $id: 1, + age: 21 + }) + + return table; + }); + + // This could be done in the same `withTable` call but its just to show that you can do it anywhere in the project + await exampleModel.withTable("table1", async (table: ExampleTable) => { + const result2 = await table.model.get(1); + console.log(result2) + /* + Expected Redis log: + + "JSON.GET" "Redis-OM:V1:test:table1:1" + + Expected result: + + JSONDocument { + '$global_prefix': 'Redis-OM', + '$prefix': 'V1', + '$model_name': 'test', + '$suffix': 'table1', + '$id': '1', + '$record_id': 'Redis-OM:V1:test:table1:1', + name: undefined, + age: 21 + } +*/ + return table; + }); + + //#endregion With Tables + + await client.disconnect(); + +})() \ No newline at end of file diff --git a/lib/client/client.ts b/lib/client/client.ts deleted file mode 100644 index a4614cc..0000000 --- a/lib/client/client.ts +++ /dev/null @@ -1,214 +0,0 @@ -import { createClient, createCluster, RediSearchSchema, SearchOptions } from 'redis' - -import { Repository } from '../repository' -import { Schema } from '../schema' -import { RedisOmError } from '../error' - -/** A conventional Redis connection. */ -export type RedisClientConnection = ReturnType - -/** A clustered Redis connection. */ -export type RedisClusterConnection = ReturnType - -/** A Redis connection, clustered or conventional. */ -export type RedisConnection = RedisClientConnection | RedisClusterConnection - -/** @internal This is a defintion for the type that calls to ft.search in Node Redis return. */ -export type SearchResults = { - total: number - documents: SearchDocument[] -} - -/** @internal This is a defintion for the return type of calls to ft.search in Node Redis. */ -export type SearchDocument = { - id: string - value: { - [key: string]: any - } -} - -/** @internal */ -export type RedisHashData = { [key: string]: string } - -/** @internal */ -export type RedisJsonData = { [key: string]: any } - -/** @internal */ -export type SearchDataStructure = 'HASH' | 'JSON' - -/** - * @internal This is a simplified redefintion of the CreateOptions type that is not exported by Node Redis. - * TODO: Remove this type once CreateOptions is exported by Node Redis. - * https://github.com/redis/node-redis/blob/master/packages/search/lib/commands/CREATE.ts#L4 - */ -export type CreateOptions = { - ON: SearchDataStructure - PREFIX: string - STOPWORDS?: string[] -} - -/** - * A Client is the starting point for working with Redis OM. Clients manage the - * connection to Redis and provide limited functionality for executing Redis commands. - * Create a client and open it before you use it: - * - * ```typescript - * const client = new Client() - * await client.open() - * ``` - * - * A Client is primarily used by a {@link Repository} which requires a client in - * its constructor. - * - * @deprecated Just used Node Redis client directly and pass it to the Repository. - */ -export class Client { - /** @internal */ - #redis?: RedisConnection - - /** Returns the underlying Node Redis connection being used. */ - get redis() { - return this.#redis - } - - /** - * Attaches an existing Node Redis connection to this Redis OM client. Closes - * any existing connection. - * - * @param connection An existing Node Redis client. - * @returns This {@link Client} instance. - */ - async use(connection: RedisConnection): Promise { - await this.close() - return this.useNoClose(connection) - } - - /** - * Attaches an existing Node Redis connection to this Redis OM client. Does - * not close any existing connection. - * - * @param connection An existing Node Redis client. - * @returns This {@link Client} instance. - */ - useNoClose(connection: RedisConnection): Client { - this.#redis = connection - return this - } - - /** - * Open a connection to Redis at the provided URL. - * - * @param url A URL to Redis as defined with the [IANA](https://www.iana.org/assignments/uri-schemes/prov/redis). - * @returns This {@link Client} instance. - */ - async open(url: string = 'redis://localhost:6379'): Promise { - if (!this.isOpen()) { - const redis = createClient({ url }) - await redis.connect() - this.#redis = redis - } - return this - } - - /** - * Creates a repository for the given schema. - * - * @param schema The schema. - * @returns A repository for the provided schema. - */ - fetchRepository(schema: Schema): Repository { - this.#validateRedisOpen() - return new Repository(schema, this) - } - - /** - * Close the connection to Redis. - */ - async close() { - if (this.#redis) await this.#redis.quit() - this.#redis = undefined - } - - /** @internal */ - async createIndex(indexName: string, schema: RediSearchSchema, options: CreateOptions) { - this.#validateRedisOpen() - await this.redis.ft.create(indexName, schema, options) - } - - /** @internal */ - async dropIndex(indexName: string) { - this.#validateRedisOpen() - await this.redis.ft.dropIndex(indexName) - } - - /** @internal */ - async search(indexName: string, query: string, options?: SearchOptions): Promise { - this.#validateRedisOpen() - if (options) return await this.redis.ft.search(indexName, query, options) - return await this.redis.ft.search(indexName, query) - } - - /** @internal */ - async unlink(...keys: string[]) { - this.#validateRedisOpen() - if (keys.length > 0) await this.redis.unlink(keys) - } - - /** @internal */ - async expire(key: string, ttl: number) { - this.#validateRedisOpen() - await this.redis.expire(key, ttl) - } - - /** @internal */ - async get(key: string): Promise { - this.#validateRedisOpen() - return this.redis.get(key) - } - - /** @internal */ - async set(key: string, value: string) { - this.#validateRedisOpen() - await this.redis.set(key, value) - } - - /** @internal */ - async hgetall(key: string): Promise { - this.#validateRedisOpen() - return this.redis.hGetAll(key) - } - - /** @internal */ - async hsetall(key: string, data: RedisHashData) { - this.#validateRedisOpen() - await this.redis - .multi() - .unlink(key) - .hSet(key, data) - .exec() - } - - /** @internal */ - async jsonget(key: string): Promise { - this.#validateRedisOpen() - const json = await this.redis.json.get(key, { path: '$' }) - return json === null ? null : (json as RedisJsonData)[0] - } - - /** @internal */ - async jsonset(key: string, data: RedisJsonData) { - this.#validateRedisOpen() - await this.redis.json.set(key, '$', data) - } - - /** - * @returns Whether a connection is already open. - */ - isOpen() { - return !!this.#redis - } - - #validateRedisOpen(): asserts this is { redis: RedisConnection } { - if (!this.redis) throw new RedisOmError("Redis connection needs to be open.") - } -} diff --git a/lib/client/index.ts b/lib/client/index.ts deleted file mode 100644 index 83dae76..0000000 --- a/lib/client/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './client' diff --git a/lib/entity/entity.ts b/lib/entity/entity.ts deleted file mode 100644 index 17234f9..0000000 --- a/lib/entity/entity.ts +++ /dev/null @@ -1,31 +0,0 @@ -/** The Symbol used to access the entity ID of an {@link Entity}. */ -export const EntityId = Symbol('entityId') - -/** The Symbol used to access the keyname of an {@link Entity}. */ -export const EntityKeyName = Symbol('entityKeyName') - -/** Defines the objects returned from calls to {@link Repository | repositories }. */ -export type Entity = EntityData & { - - /** The unique ID of the {@link Entity}. Access using the {@link EntityId} Symbol. */ - [EntityId]?: string - - /** The key the {@link Entity} is stored under inside of Redis. Access using the {@link EntityKeyName} Symbol. */ - [EntityKeyName]?: string -} - -/** The free-form data associated with an {@link Entity}. */ -export type EntityData = { - [key: string]: EntityDataValue | EntityData | Array -} - -/** Valid types for values in an {@link Entity}. */ -export type EntityDataValue = string | number | boolean | Date | Point | null | undefined - -/** Defines a point on the globe using longitude and latitude. */ -export type Point = { - /** The longitude of the point. */ - longitude: number - /** The latitude of the point. */ - latitude: number -} diff --git a/lib/entity/index.ts b/lib/entity/index.ts deleted file mode 100644 index 796a396..0000000 --- a/lib/entity/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './entity' diff --git a/lib/error/index.ts b/lib/error/index.ts deleted file mode 100644 index 3007917..0000000 --- a/lib/error/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -export * from './invalid-input' -export * from './invalid-schema' -export * from './invalid-value' -export * from './point-out-of-range' -export * from './redis-om-error' -export * from './search-error' diff --git a/lib/error/invalid-input.ts b/lib/error/invalid-input.ts deleted file mode 100644 index b813ed8..0000000 --- a/lib/error/invalid-input.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { RedisOmError } from './redis-om-error' -import { Field } from '../schema' - -export class InvalidInput extends RedisOmError {} - -export class NullJsonInput extends InvalidInput { - - #field - - constructor(field: Field) { - const message = `Null or undefined found in field '${field.name}' of type '${field.type}' in JSON at "${field.jsonPath}".` - super(message) - this.#field = field - } - - get fieldName() { return this.#field.name } - get fieldType() { return this.#field.type } - get jsonPath() { return this.#field.jsonPath } -} - -export class InvalidJsonInput extends InvalidInput { - - #field - - constructor(field: Field) { - const message = `Unexpected value for field '${field.name}' of type '${field.type}' in JSON at "${field.jsonPath}".` - super(message) - this.#field = field - } - - get fieldName() { return this.#field.name } - get fieldType() { return this.#field.type } - get jsonPath() { return this.#field.jsonPath } -} - -export class InvalidHashInput extends InvalidInput { - - #field - - constructor(field: Field) { - const message = `Unexpected value for field '${field.name}' of type '${field.type}' in Hash.` - super(message) - this.#field = field - } - - get fieldName() { return this.#field.name } - get fieldType() { return this.#field.type } -} - -export class NestedHashInput extends InvalidInput { - - #property - - constructor(property: string) { - const message = `Unexpected object in Hash at property '${property}'. You can not store a nested object in a Redis Hash.` - super(message) - this.#property = property - } - - get field() { return this.#property } -} - -export class ArrayHashInput extends InvalidInput { - - #property - - constructor(property: string) { - const message = `Unexpected array in Hash at property '${property}'. You can not store an array in a Redis Hash without defining it in the Schema.` - super(message) - this.#property = property - } - - get field() { return this.#property } -} diff --git a/lib/error/invalid-schema.ts b/lib/error/invalid-schema.ts deleted file mode 100644 index 43ad359..0000000 --- a/lib/error/invalid-schema.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { RedisOmError } from './redis-om-error' - -export class InvalidSchema extends RedisOmError {} diff --git a/lib/error/invalid-value.ts b/lib/error/invalid-value.ts deleted file mode 100644 index 97af80b..0000000 --- a/lib/error/invalid-value.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { RedisOmError } from './redis-om-error' -import { Field } from '../schema' - -export class InvalidValue extends RedisOmError {} - -export class NullJsonValue extends InvalidValue { - - #field - - constructor(field: Field) { - const message = `Null or undefined found in field '${field.name}' of type '${field.type}' from JSON path "${field.jsonPath}" in Redis.` - super(message) - this.#field = field - } - - get fieldName() { return this.#field.name } - get fieldType() { return this.#field.type } - get jsonPath() { return this.#field.jsonPath } -} - -export class InvalidJsonValue extends InvalidValue { - - #field - - constructor(field: Field) { - const message = `Unexpected value for field '${field.name}' of type '${field.type}' from JSON path "${field.jsonPath}" in Redis.` - super(message) - this.#field = field - } - - get fieldName() { return this.#field.name } - get fieldType() { return this.#field.type } - get jsonPath() { return this.#field.jsonPath } -} - -export class InvalidHashValue extends InvalidValue { - - #field - - constructor(field: Field) { - const message = `Unexpected value for field '${field.name}' of type '${field.type}' from Hash field "${field.hashField}" read from Redis.` - super(message) - this.#field = field - } - - get fieldName() { return this.#field.name } - get fieldType() { return this.#field.type } - get hashField() { return this.#field.hashField } -} diff --git a/lib/error/point-out-of-range.ts b/lib/error/point-out-of-range.ts deleted file mode 100644 index 4aa5587..0000000 --- a/lib/error/point-out-of-range.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { RedisOmError } from './redis-om-error' -import { Point } from "../entity" - -export class PointOutOfRange extends RedisOmError { - - #latitude - #longitude - - constructor(point: Point) { - super("Points must be between ±85.05112878 latitude and ±180 longitude.") - this.#longitude = point.longitude - this.#latitude = point.latitude - } - - get point() { - return { longitude: this.#longitude, latitude: this.#latitude } - } -} diff --git a/lib/error/redis-om-error.ts b/lib/error/redis-om-error.ts deleted file mode 100644 index a19f840..0000000 --- a/lib/error/redis-om-error.ts +++ /dev/null @@ -1 +0,0 @@ -export class RedisOmError extends Error {} diff --git a/lib/error/search-error.ts b/lib/error/search-error.ts deleted file mode 100644 index 7f79b29..0000000 --- a/lib/error/search-error.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { RedisOmError } from './redis-om-error' - -export class SearchError extends RedisOmError {} - -export class SemanticSearchError extends SearchError {} - -export class FieldNotInSchema extends SearchError { - - #field - - constructor(fieldName: string) { - super(`The field '${fieldName}' is not part of the schema and thus cannot be used to search.`) - this.#field = fieldName - } - - get field() { - return this.#field - } - -} diff --git a/lib/index.ts b/lib/index.ts deleted file mode 100644 index 18558af..0000000 --- a/lib/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -export { Client, RedisClientConnection, RedisConnection, RedisClusterConnection } from './client' -export { Entity, EntityData, EntityDataValue, EntityId, EntityKeyName, Point } from './entity' -export * from './error' -export { Field } from './schema/field' -export { Schema } from './schema/schema' -export { DataStructure, IdStrategy, SchemaOptions, StopWordOptions } from './schema/options' -export { AllFieldDefinition, BooleanFieldDefinition, CommonFieldDefinition, DateFieldDefinition, FieldDefinition, - FieldType, NumberFieldDefinition, PointFieldDefinition, SchemaDefinition, StringArrayFieldDefinition, - StringFieldDefinition, TextFieldDefinition } from './schema/definitions' -export { AbstractSearch, Circle, CircleFunction, RawSearch, Search, SubSearchFunction, Where, WhereField } from './search' -export { Repository } from './repository' diff --git a/lib/indexer/index-builder.ts b/lib/indexer/index-builder.ts deleted file mode 100644 index 640ab7c..0000000 --- a/lib/indexer/index-builder.ts +++ /dev/null @@ -1,156 +0,0 @@ -import { RediSearchSchema, SchemaFieldTypes } from 'redis' - -import { Schema } from '../schema/schema' -import { Field } from '../schema/field' - -const entryBuilders = { HASH: addHashEntry, JSON: addJsonEntry } - -export function buildRediSearchSchema(schema: Schema): RediSearchSchema { - const addEntry = entryBuilders[schema.dataStructure] - return schema.fields.reduce(addEntry, {}) -} - -function addHashEntry(schema: RediSearchSchema, field: Field): RediSearchSchema { - const hashField = field.hashField - - switch (field.type) { - case 'boolean': - schema[hashField] = buildHashBoolean(field) - break - case 'date': - schema[hashField] = buildDateNumber(field) - break - case 'number': - schema[hashField] = buildDateNumber(field) - break - case 'point': - schema[hashField] = buildPoint(field) - break - case 'string[]': - case 'string': - schema[hashField] = buildHashString(field) - break - case 'text': - schema[hashField] = buildText(field) - break - } - - return schema -} - -function addJsonEntry(schema: RediSearchSchema, field: Field): RediSearchSchema { - const jsonPath = field.jsonPath - - switch (field.type) { - case 'boolean': - schema[jsonPath] = buildJsonBoolean(field) - break - case 'date': - schema[jsonPath] = buildDateNumber(field) - break - case 'number': - schema[jsonPath] = buildDateNumber(field) - break - case 'point': - schema[jsonPath] = buildPoint(field) - break - case 'string[]': - case 'string': - schema[jsonPath] = buildJsonString(field) - break - case 'text': - schema[jsonPath] = buildText(field) - break - } - - return schema -} - -function buildHashBoolean(field: Field): any { - const schemaField = { type: SchemaFieldTypes.TAG, AS: field.name } - addSortable(schemaField, field) - addIndexed(schemaField, field) - return schemaField -} - -function buildJsonBoolean(field: Field): any { - if (field.sortable) - console.warn(`You have marked a boolean field as sortable but RediSearch doesn't support the SORTABLE argument on a TAG for JSON. Ignored.`) - const schemaField = { type: SchemaFieldTypes.TAG, AS: field.name } - addIndexed(schemaField, field) - return schemaField -} - -function buildDateNumber(field: Field): any { - const schemaField = { type: SchemaFieldTypes.NUMERIC, AS: field.name } - addSortable(schemaField, field) - addIndexed(schemaField, field) - return schemaField -} - -function buildPoint(field: Field): any { - const schemaField = { type: SchemaFieldTypes.GEO, AS: field.name } - addIndexed(schemaField, field) - return schemaField -} - -function buildHashString(field: Field): any { - const schemaField = { type: SchemaFieldTypes.TAG, AS: field.name } - addCaseInsensitive(schemaField, field), - addSeparable(schemaField, field), - addSortable(schemaField, field) - addIndexed(schemaField, field) - return schemaField -} - -function buildJsonString(field: Field): any { - if (field.sortable) - console.warn(`You have marked a ${field.type} field as sortable but RediSearch doesn't support the SORTABLE argument on a TAG for JSON. Ignored.`) - const schemaField = { type: SchemaFieldTypes.TAG, AS: field.name } - addCaseInsensitive(schemaField, field), - addSeparable(schemaField, field), - addIndexed(schemaField, field) - return schemaField -} - -function buildText(field: Field): any { - const schemaField = { type: SchemaFieldTypes.TEXT, AS: field.name } - addSortable(schemaField, field) - addStemming(schemaField, field) - addIndexed(schemaField, field) - addPhonetic(schemaField, field) - addWeight(schemaField, field) - return schemaField -} - -function addCaseInsensitive(schemaField: any, field: Field) { - if (field.caseSensitive) schemaField.CASESENSITIVE = true -} - -function addIndexed(schemaField: any, field: Field) { - if (!field.indexed) schemaField.NOINDEX = true -} - -function addStemming(schemaField: any, field: Field) { - if (!field.stemming) schemaField.NOSTEM = true -} - -function addPhonetic(schemaField: any, field: Field) { - if (field.matcher) schemaField.PHONETIC = field.matcher -} - -function addSeparable(schemaField: any, field: Field) { - schemaField.SEPARATOR = field.separator -} - -function addSortable(schemaField: any, field: Field) { - if (field.normalized) { - if (field.sortable) schemaField.SORTABLE = true - } else { - schemaField.SORTABLE = 'UNF' - } -} - -function addWeight(schemaField: any, field: Field) { - if (field.weight) schemaField.WEIGHT = field.weight -} diff --git a/lib/indexer/index.ts b/lib/indexer/index.ts deleted file mode 100644 index 299c10d..0000000 --- a/lib/indexer/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './index-builder' diff --git a/lib/repository/index.ts b/lib/repository/index.ts deleted file mode 100644 index d440231..0000000 --- a/lib/repository/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './repository' diff --git a/lib/repository/repository.ts b/lib/repository/repository.ts deleted file mode 100644 index b74ca62..0000000 --- a/lib/repository/repository.ts +++ /dev/null @@ -1,341 +0,0 @@ -import { Client, CreateOptions, RedisConnection, RedisHashData, RedisJsonData } from '../client' -import { Entity, EntityId, EntityKeyName } from '../entity' -import { buildRediSearchSchema } from '../indexer' -import { Schema } from '../schema' -import { Search, RawSearch } from '../search' -import { fromRedisHash, fromRedisJson, toRedisHash, toRedisJson } from '../transformer' - -/** - * A repository is the main interaction point for reading, writing, and - * removing {@link Entity | Entities} from Redis. Create one by calling - * {@link Client.fetchRepository} and passing in a {@link Schema}. Then - * use the {@link Repository#fetch}, {@link Repository#save}, and - * {@link Repository#remove} methods to manage your data: - * - * ```typescript - * const repository = client.fetchRepository(schema) - * - * const foo = await repository.fetch('01FK6TCJBDK41RJ766A4SBWDJ9') - * foo.aString = 'bar' - * foo.aBoolean = false - * await repository.save(foo) - * ``` - * - * Use the repository to create a new instance of an {@link Entity} - * before you save it: - * - * ```typescript - * const foo = await repository.createEntity() - * foo.aString = 'bar' - * foo.aBoolean = false - * await repository.save(foo) - * ``` - * - * If you want to use the {@link Repository#search} method, you need to create an index - * first, and you need RediSearch or RedisJSON installed on your instance of Redis: - * - * ```typescript - * await repository.createIndex() - * const entities = await repository.search() - * .where('aString').eq('bar') - * .and('aBoolean').is.false().returnAll() - * ``` - */ -export class Repository { - - // NOTE: Not using "#" private as the spec needs to check calls on this class. Will be resolved when Client class is removed. - private client: Client - #schema: Schema - - /** - * Creates a new {@link Repository}. - * - * @param schema The schema defining that data in the repository. - * @param client A client to talk to Redis. - */ - constructor(schema: Schema, clientOrConnection: Client | RedisConnection) { - this.#schema = schema - if (clientOrConnection instanceof Client) { - this.client = clientOrConnection - } else { - this.client = new Client() - this.client.useNoClose(clientOrConnection) - } - } - - /** - * Creates an index in Redis for use by the {@link Repository#search} method. - * Does not create a new index if the index hasn't changed. Requires that - * RediSearch and RedisJSON are installed on your instance of Redis. - */ - async createIndex() { - - const currentIndexHash = await this.client.get(this.#schema.indexHashName) - const incomingIndexHash = this.#schema.indexHash - - if (currentIndexHash !== incomingIndexHash) { - - await this.dropIndex() - - const { - indexName, indexHashName, dataStructure, - schemaName: prefix, useStopWords, stopWords - } = this.#schema - - const schema = buildRediSearchSchema(this.#schema) - const options: CreateOptions = { - ON: dataStructure, - PREFIX: `${prefix}:` - } - - if (useStopWords === 'OFF') { - options.STOPWORDS = [] - } else if (useStopWords === 'CUSTOM') { - options.STOPWORDS = stopWords - } - - await Promise.all([ - this.client.createIndex(indexName, schema, options), - this.client.set(indexHashName, incomingIndexHash) - ]) - } - } - - /** - * Removes an existing index from Redis. Use this method if you want to swap out your index - * because your {@link Entity} has changed. Requires that RediSearch and RedisJSON are installed - * on your instance of Redis. - */ - async dropIndex() { - try { - await Promise.all([ - this.client.unlink(this.#schema.indexHashName), - this.client.dropIndex(this.#schema.indexName) - ]) - } catch (e) { - /* NOTE: It would be better if this error handler was only around the call - to `.dropIndex`. Might muss up the code a bit though. Life is full of - tough choices. */ - if (e instanceof Error && e.message === "Unknown Index name") { - // no-op: the thing we are dropping doesn't exist - } else { - throw e - } - } - } - - /** - * Insert or update an {@link Entity} to Redis using its entityId property - * if present. If it's not, one is generated. - * - * @param entity The Entity to save. - * @returns A copy of the provided Entity with EntityId and EntityKeyName properties added. - */ - async save(entity: Entity): Promise - - /** - * Insert or update the {@link Entity} to Redis using the provided entityId. - * - * @param id The id to save the Entity under. - * @param entity The Entity to save. - * @returns A copy of the provided Entity with EntityId and EntityKeyName properties added. - */ - async save(id: string, entity: Entity): Promise - - async save(entityOrId: Entity | string, maybeEntity?: Entity): Promise { - let entity: Entity | undefined - let entityId: string | undefined - - if (typeof entityOrId !== 'string') { - entity = entityOrId - entityId = entity[EntityId] ?? await this.#schema.generateId() - } else { - entity = maybeEntity - entityId = entityOrId - } - - const keyName = `${this.#schema.schemaName}:${entityId}` - const clonedEntity = { ...entity, [EntityId]: entityId, [EntityKeyName]: keyName } - await this.writeEntity(clonedEntity) - - return clonedEntity - } - - /** - * Read and return an {@link Entity} from Redis for the given id. If - * the {@link Entity} is not found, returns an empty {@link Entity}. - * - * @param id The ID of the {@link Entity} you seek. - * @returns The matching Entity. - */ - async fetch(id: string): Promise - - /** - * Read and return the {@link Entity | Entities} from Redis with the given IDs. If - * a particular {@link Entity} is not found, returns that {@link Entity} as empty. - * - * @param ids The IDs of the {@link Entity | Entities} you seek. - * @returns The matching Entities. - */ - async fetch(...ids: string[]): Promise - - /** - * Read and return the {@link Entity | Entities} from Redis with the given IDs. If - * a particular {@link Entity} is not found, returns that {@link Entity} as empty. - * - * @param ids The IDs of the {@link Entity | Entities} you seek. - * @returns The matching Entities. - */ - async fetch(ids: string[]): Promise - - async fetch(ids: string | string[]): Promise { - if (arguments.length > 1) return this.readEntities([...arguments]) - if (Array.isArray(ids)) return this.readEntities(ids) - - const [entity] = await this.readEntities([ids]) - return entity! - } - - /** - * Remove an {@link Entity} from Redis for the given id. If the {@link Entity} is - * not found, does nothing. - * - * @param id The ID of the {@link Entity} you wish to delete. - */ - async remove(id: string): Promise - - /** - * Remove the {@link Entity | Entities} from Redis for the given ids. If a - * particular {@link Entity} is not found, does nothing. - * - * @param ids The IDs of the {@link Entity | Entities} you wish to delete. - */ - async remove(...ids: string[]): Promise - - /** - * Remove the {@link Entity | Entities} from Redis for the given ids. If a - * particular {@link Entity} is not found, does nothing. - * - * @param ids The IDs of the {@link Entity | Entities} you wish to delete. - */ - async remove(ids: string[]): Promise - - async remove(ids: string | string[]): Promise { - // TODO: clean code - const keys = arguments.length > 1 - ? this.makeKeys([...arguments]) - : Array.isArray(ids) - ? this.makeKeys(ids) - : ids ? this.makeKeys([ids]) : [] - - if (keys.length === 0) return - await this.client.unlink(...keys) - } - - /** - * Set the time to live of the {@link Entity}. If the {@link Entity} is not - * found, does nothing. - * - * @param id The ID of the {@link Entity} to set and expiration for. - * @param ttlInSeconds The time to live in seconds. - */ - async expire(id: string, ttlInSeconds: number): Promise - - /** - * Set the time to live of the {@link Entity | Entities} in Redis with the given - * ids. If a particular {@link Entity} is not found, does nothing. - * - * @param ids The IDs of the {@link Entity | Entities} you wish to delete. - */ - async expire(ids: string[], ttlInSeconds: number): Promise - - async expire(idOrIds: string | string[], ttlInSeconds: number): Promise { - const ids = typeof(idOrIds) === 'string' ? [ idOrIds ] : idOrIds - await Promise.all( - ids.map(id => { - const key = this.makeKey(id) - return this.client.expire(key, ttlInSeconds) - }) - ) - } - - /** - * Kicks off the process of building a query. Requires that RediSearch (and optionally - * RedisJSON) be installed on your instance of Redis. - * - * @returns A {@link Search} object. - */ - search(): Search { - return new Search(this.#schema, this.client) - } - - /** - * Creates a search that bypasses Redis OM and instead allows you to execute a raw - * RediSearch query. Requires that RediSearch (and optionally RedisJSON) be installed - * on your instance of Redis. - * - * Refer to https://redis.io/docs/stack/search/reference/query_syntax/ for details on - * RediSearch query syntax. - * - * @query The raw RediSearch query you want to rune. - * @returns A {@link RawSearch} object. - */ - searchRaw(query: string): RawSearch { - return new RawSearch(this.#schema, this.client, query) - } - - private async writeEntity(entity: Entity): Promise { - return this.#schema.dataStructure === 'HASH' ? this.writeEntityToHash(entity) : this.writeEntityToJson(entity) - } - - private async readEntities(ids: string[]): Promise { - return this.#schema.dataStructure === 'HASH' ? this.readEntitiesFromHash(ids) : this.readEntitiesFromJson(ids) - } - - // TODO: make this actually private... like with # - private async writeEntityToHash(entity: Entity): Promise { - const keyName = entity[EntityKeyName]! - const hashData: RedisHashData = toRedisHash(this.#schema, entity) - if (Object.keys(hashData).length === 0) { - await this.client.unlink(keyName) - } else { - await this.client.hsetall(keyName, hashData) - } - } - - private async readEntitiesFromHash(ids: string[]): Promise { - return Promise.all( - ids.map(async (entityId) => { - const keyName = this.makeKey(entityId) - const hashData = await this.client.hgetall(keyName) - const entityData = fromRedisHash(this.#schema, hashData) - const entity = { ...entityData, [EntityId]: entityId, [EntityKeyName]: keyName } - return entity - })) - } - - private async writeEntityToJson(entity: Entity): Promise { - const keyName = entity[EntityKeyName]! - const jsonData: RedisJsonData = toRedisJson(this.#schema, entity) - await this.client.jsonset(keyName, jsonData) - } - - private async readEntitiesFromJson(ids: string[]): Promise { - return Promise.all( - ids.map(async (entityId) => { - const keyName = this.makeKey(entityId) - const jsonData = await this.client.jsonget(keyName) ?? {} - const entityData = fromRedisJson(this.#schema, jsonData) - const entity = {...entityData, [EntityId]: entityId, [EntityKeyName]: keyName } - return entity - })) - } - - private makeKeys(ids: string[]): string[] { - return ids.map(id => this.makeKey(id)) - } - - private makeKey(id: string): string { - return `${this.#schema.schemaName}:${id}` - } -} diff --git a/lib/schema/definitions.ts b/lib/schema/definitions.ts deleted file mode 100644 index 600b84d..0000000 --- a/lib/schema/definitions.ts +++ /dev/null @@ -1,118 +0,0 @@ -/** Valid field types for a {@link FieldDefinition}. */ -export type FieldType = 'boolean' | 'date' | 'number' | 'point' | 'string' | 'string[]' | 'text' - -/** All configuration properties that any field might have, regardless of type. */ -export type AllFieldDefinition = { - - /** The type of the field (i.e. string, number, boolean, etc.) */ - type: FieldType - - /** - * The default field name in Redis is the property name defined in the - * {@link SchemaDefinition}. Overrides the field name for a Hash to this - * value or in the case of JSON documents, sets the JSONPath to this - * value preceded by `$.`. Overridden by {@link field} and/or {@link path} - * settings. - * @deprecated - */ - alias?: string - - /** - * Is this field indexed and thus searchable with Redis OM. Defaults - * to true. - */ - indexed?: boolean - - /** - * The field name used to store this in a Redis Hash. Defaults to the - * name used in the {@link SchemaDefinition} or the {@link alias} - * property. - */ - field?: string - - /** - * The JSONPath expression this field references. Used only by search - * and only for JSON documents. Defaults to the name used in the - * {@link SchemaDefinition} or the {@link alias} property prefixed - * with `$.` . - */ - path?: string - - /** Enables sorting by this field. */ - sortable?: boolean - - /** Is the original case of this field indexed with Redis OM. Defaults to false. */ - caseSensitive?: boolean - - /** Is this (sortable) field normalized when indexed. Defaults to true. */ - normalized?: boolean - - /** - * Due to how RediSearch works, strings and arrays are sometimes stored the same in Redis, as a - * simple string. This is the separator used to split those strings when it is an array. If your - * StringField contains this separator, this can cause problems. You can change it here to avoid - * those problems. Defaults to `|`. - */ - separator?: string - - /** - * Enables setting the phonetic matcher to use, supported matchers are: - * dm:en - Double Metaphone for English - * dm:fr - Double Metaphone for French - * dm:pt - Double Metaphone for Portuguese - * dm:es - Double Metaphone for Spanish - */ - matcher?: 'dm:en' | 'dm:fr' | 'dm:pt' | 'dm:es' - - /** Is word stemming applied to this field with Redis OM. Defaults to true. */ - stemming?: boolean - - /** Enables setting the weight to apply to a text field */ - weight?: number -} - -/** The configuration properties that all fields have in common. */ -export type CommonFieldDefinition = Pick - -/** A field representing a boolean. */ -export type BooleanFieldDefinition = { type: 'boolean' } - & CommonFieldDefinition - & Pick - -/** A field representing a date/time. */ -export type DateFieldDefinition = { type: 'date' } - & CommonFieldDefinition - & Pick - -/** A field representing a number. */ -export type NumberFieldDefinition = { type: 'number' } - & CommonFieldDefinition - & Pick - -/** A field representing a point on the globe. */ -export type PointFieldDefinition = { type: 'point' } - & CommonFieldDefinition - -/** A field representing an array of strings. */ -export type StringArrayFieldDefinition = { type: 'string[]' } - & CommonFieldDefinition - & Pick - -/** A field representing a whole string. */ -export type StringFieldDefinition = { type: 'string' } - & CommonFieldDefinition - & Pick - -/** A field representing searchable text. */ -export type TextFieldDefinition = { type: 'text' } - & CommonFieldDefinition - & Pick - -/** Contains instructions telling how to map a property on an {@link Entity} to Redis. */ -export type FieldDefinition = - BooleanFieldDefinition | DateFieldDefinition | NumberFieldDefinition | - PointFieldDefinition | StringArrayFieldDefinition | StringFieldDefinition | - TextFieldDefinition - -/** Group of {@link FieldDefinition}s that define the schema for an {@link Entity}. */ -export type SchemaDefinition = Record diff --git a/lib/schema/field.ts b/lib/schema/field.ts deleted file mode 100644 index 7479b66..0000000 --- a/lib/schema/field.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { AllFieldDefinition, FieldDefinition, FieldType } from './definitions' - -/** - * Describes a field in a {@link Schema}. - */ -export class Field { - - #name: string - #definition: AllFieldDefinition - - /** - * Creates a Field. - * - * @param name The name of the Field. - * @param definition The underlying {@link FieldDefinition}. - */ - constructor(name: string, definition: FieldDefinition) { - this.#name = name - this.#definition = definition - } - - /** The name of the field. */ - get name(): string { - return this.#name - } - - /** The {@link FieldType | type} of the field. */ - get type(): FieldType { - return this.#definition.type - } - - /** The field name used to store this {@link Field} in a Hash. */ - get hashField(): string { - return this.#definition.field ?? this.#definition.alias ?? this.name - } - - /** The JSONPath used to store this {@link Field} in a JSON document. */ - get jsonPath(): string { - if (this.#definition.path) return this.#definition.path - const alias = this.#definition.alias ?? this.name - return this.type === 'string[]' ? `$.${alias}[*]` : `$.${alias}` - } - - /** The separator for string[] fields when stored in Hashes. */ - get separator(): string { - return this.#definition.separator ?? '|' - } - - /** Indicates that the field as sortable. */ - get sortable(): boolean { - return this.#definition.sortable ?? false - } - - /** The case-sensitivity of the field. */ - get caseSensitive(): boolean { - return this.#definition.caseSensitive ?? false - } - - /** Indicates the field as being indexed—and thus queryable—by RediSearch. */ - get indexed(): boolean { - return this.#definition.indexed ?? true - } - - /** Indicates that the field as indexed with stemming support. */ - get stemming(): boolean { - return this.#definition.stemming ?? true - } - - /** Indicates that the field is normalized. Ignored if sortable is false. */ - get normalized(): boolean { - return this.#definition.normalized ?? true - } - - /** The search weight of the field. */ - get weight(): number | null { - return this.#definition.weight ?? null - } - - /** The phonetic matcher for the field. */ - get matcher(): string | null { - return this.#definition.matcher ?? null - } -} diff --git a/lib/schema/index.ts b/lib/schema/index.ts deleted file mode 100644 index 155360e..0000000 --- a/lib/schema/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './definitions' -export * from './field' -export * from './options' -export * from './schema' diff --git a/lib/schema/options.ts b/lib/schema/options.ts deleted file mode 100644 index e4a3806..0000000 --- a/lib/schema/options.ts +++ /dev/null @@ -1,52 +0,0 @@ -/** The type of data structure in Redis to map objects to. */ -export type DataStructure = 'HASH' | 'JSON'; - -/** A function that generates random entityIds. */ -export type IdStrategy = () => Promise; - -/** Valid values for how to use stop words for a given {@link Schema}. */ -export type StopWordOptions = 'OFF' | 'DEFAULT' | 'CUSTOM'; - -/** Configuration options for a {@link Schema}. */ -export type SchemaOptions = { - - /** - * The name used by RediSearch to store the index for this {@link Schema}. Defaults - * to prefix followed by `:index`. So, for a prefix of `Foo`, it would use `Foo:index`. - */ - indexName?: string - - /** - * The name used by Redis OM to store the hash of the index for this {@link Schema}. - * Defaults to prefix followed by `:index:hash`. So, for a prefix of `Foo`, it would - * use `Foo:index:hash`. - */ - indexHashName?: string - - /** The data structure used to store the {@link Entity} in Redis. Can be set - * to either `JSON` or `HASH`. Defaults to JSON. - */ - dataStructure?: DataStructure - - /** - * A function that generates a random entityId. Defaults to a function that generates - * [ULIDs](https://github.com/ulid/spec). Combined with prefix to generate a Redis key. - * If prefix is `Foo` and idStratgey returns `12345` then the generated key would be - * `Foo:12345`. - */ - idStrategy?: IdStrategy - - /** - * Configures the usage of stop words. Valid values are `OFF`, `DEFAULT`, and `CUSTOM`. - * Setting this to `OFF` disables all stop words. Setting this to `DEFAULT` uses the - * stop words intrinsic to RediSearch. Setting this to `CUSTOM` tells RediSearch to - * use the stop words in `stopWords`. - */ - useStopWords?: StopWordOptions - - /** - * Stop words to be used by this schema. If `useStopWords` is - * anything other than `CUSTOM`, this option is ignored. - */ - stopWords?: Array -} diff --git a/lib/schema/schema.ts b/lib/schema/schema.ts deleted file mode 100644 index 0b66380..0000000 --- a/lib/schema/schema.ts +++ /dev/null @@ -1,165 +0,0 @@ -import { createHash } from 'crypto' -import { ulid } from 'ulid' - -import { Entity } from "../entity" - -import { IdStrategy, DataStructure, StopWordOptions, SchemaOptions } from './options' - -import { SchemaDefinition } from './definitions' -import { Field } from './field' -import { InvalidSchema } from '../error' - - -/** - * Defines a schema that determines how an {@link Entity} is mapped - * to Redis data structures. Construct by passing in a schema name, - * a {@link SchemaDefinition}, and optionally {@link SchemaOptions}: - * - * ```typescript - * const schema = new Schema('foo', { - * aString: { type: 'string' }, - * aNumber: { type: 'number' }, - * aBoolean: { type: 'boolean' }, - * someText: { type: 'text' }, - * aPoint: { type: 'point' }, - * aDate: { type: 'date' }, - * someStrings: { type: 'string[]' } - * }, { - * dataStructure: 'HASH' - * }) - * ``` - * - * A Schema is primarily used by a {@link Repository} which requires a Schema in - * its constructor. - */ -export class Schema { - - #schemaName: string - #fieldsByName: Record = {} - #definition: SchemaDefinition - #options?: SchemaOptions - - /** - * Constructs a Schema. - * - * @param schemaName The name of the schema. Prefixes the ID when creating Redis keys. - * @param schemaDef Defines all of the fields for the Schema and how they are mapped to Redis. - * @param options Additional options for this Schema. - */ - constructor(schemaName: string, schemaDef: SchemaDefinition, options?: SchemaOptions) { - this.#schemaName = schemaName - this.#definition = schemaDef - this.#options = options - - this.#validateOptions() - this.#createFields() - } - - /** - * The name of the schema. Prefixes the ID when creating Redis keys. Combined - * with the results of idStrategy to generate a key. If name is `foo` and - * idStrategy returns `12345` then the generated key would be `foo:12345`. - */ - get schemaName(): string { - return this.#schemaName - } - - /** The {@link Field | Fields} defined by this Schema. */ - get fields(): Field[] { - return Object.entries(this.#fieldsByName).map(([_name, field]) => field) - } - - /** - * Gets a single {@link Field} defined by this Schema. - * - * @param name The name of the {@link Field} in this Schema. - * @returns The {@link Field}, or null of not found. - */ - fieldByName(name: string): Field | null { - return this.#fieldsByName[name] ?? null - } - - /** The configured name for the RediSearch index for this Schema. */ - get indexName(): string { return this.#options?.indexName ?? `${this.schemaName}:index` } - - /** The configured name for the RediSearch index hash for this Schema. */ - get indexHashName(): string { return this.#options?.indexHashName ?? `${this.schemaName}:index:hash` } - - /** - * The configured data structure, a string with the value of either `HASH` or `JSON`, - * that this Schema uses to store {@link Entity | Entities} in Redis. - */ - get dataStructure(): DataStructure { return this.#options?.dataStructure ?? 'JSON' } - - /** - * The configured usage of stop words, a string with the value of either `OFF`, `DEFAULT`, - * or `CUSTOM`. See {@link SchemaOptions} for more details. - */ - get useStopWords(): StopWordOptions { return this.#options?.useStopWords ?? 'DEFAULT' } - - /** - * The configured stop words. Ignored if {@link Schema.useStopWords} is anything other - * than `CUSTOM`. - */ - get stopWords(): Array { return this.#options?.stopWords ?? [] } - - /** - * Generates a unique string using the configured {@link IdStrategy}. - * - * @returns The generated id. - */ - async generateId(): Promise { - const ulidStrategy = () => ulid() - return await (this.#options?.idStrategy ?? ulidStrategy)() - } - - /** - * A hash for this Schema that is used to determine if the Schema has been - * changed when calling {@link Repository#createIndex}. - */ - get indexHash(): string { - - const data = JSON.stringify({ - definition: this.#definition, - prefix: this.schemaName, - indexName: this.indexName, - indexHashName: this.indexHashName, - dataStructure: this.dataStructure, - useStopWords: this.useStopWords, - stopWords: this.stopWords - }) - - return createHash('sha1').update(data).digest('base64') - } - - #createFields() { - return Object.entries(this.#definition).forEach(([fieldName, fieldDef]) => { - const field = new Field(fieldName, fieldDef) - this.#validateField(field) - this.#fieldsByName[fieldName] = field - }) - } - - #validateOptions() { - const { dataStructure, useStopWords } = this - - if (dataStructure !== 'HASH' && dataStructure !== 'JSON') - throw new InvalidSchema(`'${dataStructure}' in an invalid data structure. Valid data structures are 'HASH' and 'JSON'.`) - - if (useStopWords !== 'OFF' && useStopWords !== 'DEFAULT' && useStopWords !== 'CUSTOM') - throw new InvalidSchema(`'${useStopWords}' in an invalid value for stop words. Valid values are 'OFF', 'DEFAULT', and 'CUSTOM'.`) - - if (this.#options?.idStrategy && typeof this.#options.idStrategy !== 'function') - throw new InvalidSchema("ID strategy must be a function that takes no arguments and returns a string.") - - if (this.schemaName === '') throw new InvalidSchema(`Schema name must be a non-empty string.`) - if (this.indexName === '') throw new InvalidSchema(`Index name must be a non-empty string.`) - } - - #validateField(field: Field) { - const { type } = field - if (type !== 'boolean' && type !== 'date' && type !== 'number' && type !== 'point' && - type !== 'string' && type !== 'string[]' && type !== 'text') - throw new InvalidSchema(`The field '${field.name}' is configured with a type of '${field.type}'. Valid types include 'boolean', 'date', 'number', 'point', 'string', 'string[]', and 'text'.`) - } -} diff --git a/lib/search/index.ts b/lib/search/index.ts deleted file mode 100644 index 45f051c..0000000 --- a/lib/search/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -export * from './results-converter' -export * from './search' -export * from './where-and' -export * from './where-boolean' -export * from './where-date' -export * from './where-field' -export * from './where-number' -export * from './where-or' -export * from './where-point' -export * from './where-string-array' -export * from './where-string' -export * from './where-text' -export * from './where' diff --git a/lib/search/results-converter.ts b/lib/search/results-converter.ts deleted file mode 100644 index e10c9ad..0000000 --- a/lib/search/results-converter.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { RedisHashData, RedisJsonData, SearchDocument, SearchResults } from "../client" -import { Entity, EntityData, EntityId, EntityKeyName } from "../entity" -import { Schema } from "../schema" -import { fromRedisHash, fromRedisJson } from "../transformer" - -export function extractCountFromSearchResults(results: SearchResults): number { - return results.total -} - -export function extractKeyNamesFromSearchResults(results: SearchResults): string[] { - return results.documents.map(document => document.id) -} - -export function extractEntityIdsFromSearchResults(schema: Schema, results: SearchResults): string[] { - const keyNames = extractKeyNamesFromSearchResults(results) - const entityIds = keyNamesToEntityIds(schema.schemaName, keyNames) - return entityIds -} - -export function extractEntitiesFromSearchResults(schema: Schema, results: SearchResults): Entity[] { - if (schema.dataStructure === 'HASH') { - return results.documents.map(document => hashDocumentToEntity(schema, document)) - } else { - return results.documents.map(document => jsonDocumentToEntity(schema, document)) - } -} - -function hashDocumentToEntity(schema: Schema, document: SearchDocument): Entity { - const keyName: string = document.id - const hashData: RedisHashData = document.value - - const entityData = fromRedisHash(schema, hashData) - const entity = enrichEntityData(schema.schemaName, keyName, entityData) - return entity -} - -function jsonDocumentToEntity(schema: Schema, document: SearchDocument): Entity { - const keyName: string = document.id - const jsonData: RedisJsonData = document.value['$'] ?? false ? JSON.parse(document.value['$']) : document.value - - const entityData = fromRedisJson(schema, jsonData) - const entity = enrichEntityData(schema.schemaName, keyName, entityData) - return entity -} - -function enrichEntityData(keyPrefix: string, keyName: string, entityData: EntityData) { - const entityId = keyNameToEntityId(keyPrefix, keyName) - const entity = { ...entityData, [EntityId]: entityId, [EntityKeyName]: keyName} - return entity -} - -function keyNamesToEntityIds(keyPrefix: string, keyNames: string[]): string[] { - return keyNames.map(keyName => keyNameToEntityId(keyPrefix, keyName)) -} - -function keyNameToEntityId(keyPrefix: string, keyName: string): string { - const escapedPrefix = keyPrefix.replace(/[/\-\\^$*+?.()|[\]{}]/g, '\\$&') - const regex = new RegExp(`^${escapedPrefix}:`) - const entityId = keyName.replace(regex, "") - return entityId -} diff --git a/lib/search/search.ts b/lib/search/search.ts deleted file mode 100644 index d0b52e9..0000000 --- a/lib/search/search.ts +++ /dev/null @@ -1,613 +0,0 @@ -import { SearchOptions } from "redis" - -import { Client } from "../client" -import { Entity } from '../entity' -import { Schema } from "../schema" - -import { Where } from './where' -import { WhereAnd } from './where-and' -import { WhereOr } from './where-or' -import { WhereField } from './where-field' -import { WhereStringArray } from './where-string-array' -import { WhereHashBoolean, WhereJsonBoolean } from './where-boolean' -import { WhereNumber } from './where-number' -import { WherePoint } from './where-point' -import { WhereString } from './where-string' -import { WhereText } from './where-text' - -import { extractCountFromSearchResults, extractEntitiesFromSearchResults, extractEntityIdsFromSearchResults, extractKeyNamesFromSearchResults } from "./results-converter" -import { FieldNotInSchema, RedisOmError, SearchError } from "../error" -import { WhereDate } from "./where-date" - -/** - * A function that takes a {@link Search} and returns a {@link Search}. Used in nested queries. - * @template TEntity The type of {@link Entity} being sought. - */ -export type SubSearchFunction = (search: Search) => Search - -type AndOrConstructor = new (left: Where, right: Where) => Where - -// This is a simplified redefintion of the SortByProperty type that is not exported by Node Redis -type SortOptions = { BY: string, DIRECTION: 'ASC' | 'DESC' } - -/** - * Abstract base class for {@link Search} and {@link RawSearch} that - * contains methods to return search results. - * @template TEntity The type of {@link Entity} being sought. - */ -export abstract class AbstractSearch { - - /** @internal */ - protected schema: Schema - - /** @internal */ - protected client: Client - - /** @internal */ - protected sortOptions?: SortOptions - - /** @internal */ - constructor(schema: Schema, client: Client) { - this.schema = schema - this.client = client - } - - /** @internal */ - abstract get query(): string - - /** - * Applies an ascending sort to the query. - * @param field The field to sort by. - * @returns this - */ - sortAscending(field: string): AbstractSearch { - return this.sortBy(field, 'ASC') - } - - /** - * Alias for {@link Search.sortDescending}. - */ - sortDesc(field: string): AbstractSearch { - return this.sortDescending(field) - } - - /** - * Applies a descending sort to the query. - * @param field The field to sort by. - * @returns this - */ - sortDescending(field: string): AbstractSearch { - return this.sortBy(field, 'DESC') - } - - /** - * Alias for {@link Search.sortAscending}. - */ - sortAsc(field: string): AbstractSearch { - return this.sortAscending(field) - } - - /** - * Applies sorting for the query. - * @param field The field to sort by. - * @param order The order of returned {@link Entity | Entities} Defaults to `ASC` (ascending) if not specified - * @returns this - */ - sortBy(fieldName: string, order: 'ASC' | 'DESC' = 'ASC'): AbstractSearch { - const field = this.schema.fieldByName(fieldName) - const dataStructure = this.schema.dataStructure - - if (!field) { - const message = `'sortBy' was called on field '${fieldName}' which is not defined in the Schema.` - console.error(message) - throw new RedisOmError(message) - } - - const type = field.type - const markedSortable = field.sortable - - const UNSORTABLE = ['point', 'string[]'] - const JSON_SORTABLE = ['number', 'text', 'date'] - const HASH_SORTABLE = ['string', 'boolean', 'number', 'text', 'date'] - - if (UNSORTABLE.includes(type)) { - const message = `'sortBy' was called on '${field.type}' field '${field.name}' which cannot be sorted.` - console.error(message) - throw new RedisOmError(message) - } - - if (dataStructure === 'JSON' && JSON_SORTABLE.includes(type) && !markedSortable) - console.warn(`'sortBy' was called on field '${field.name}' which is not marked as sortable in the Schema. This may result is slower searches. If possible, mark the field as sortable in the Schema.`) - - if (dataStructure === 'HASH' && HASH_SORTABLE.includes(type) && !markedSortable) - console.warn(`'sortBy' was called on field '${field.name}' which is not marked as sortable in the Schema. This may result is slower searches. If possible, mark the field as sortable in the Schema.`) - - this.sortOptions = { BY: field.name, DIRECTION: order } - - return this - } - - /** - * Finds the {@link Entity} with the minimal value for a field. - * @param field The field with the minimal value. - * @returns The {@link Entity} with the minimal value - */ - async min(field: string): Promise { - return await this.sortBy(field, 'ASC').first() - } - - /** - * Finds the entity ID with the minimal value for a field. - * @param field The field with the minimal value. - * @returns The entity ID with the minimal value - */ - async minId(field: string): Promise { - return await this.sortBy(field, 'ASC').firstId() - } - - /** - * Finds the key name in Redis with the minimal value for a field. - * @param field The field with the minimal value. - * @returns The key name with the minimal value - */ - async minKey(field: string): Promise { - return await this.sortBy(field, 'ASC').firstKey() - } - - /** - * Finds the {@link Entity} with the maximal value for a field. - * @param field The field with the maximal value. - * @returns The entity ID {@link Entity} with the maximal value - */ - async max(field: string): Promise { - return await this.sortBy(field, 'DESC').first() - } - - /** - * Finds the entity ID with the maximal value for a field. - * @param field The field with the maximal value. - * @returns The entity ID with the maximal value - */ - async maxId(field: string): Promise{ - return await this.sortBy(field, 'DESC').firstId() - } - - /** - * Finds the key name in Redis with the maximal value for a field. - * @param field The field with the maximal value. - * @returns The key name with the maximal value - */ - async maxKey(field: string): Promise { - return await this.sortBy(field, 'DESC').firstKey() - } - - /** - * Returns the number of {@link Entity | Entities} that match this query. - * @returns - */ - async count(): Promise { - const searchResults = await this.callSearch() - return extractCountFromSearchResults(searchResults) - } - - /** - * Returns a page of {@link Entity | Entities} that match this query. - * @param offset The offset for where to start returning {@link Entity | Entities}. - * @param count The number of {@link Entity | Entities} to return. - * @returns An array of {@link Entity | Entities} matching the query. - */ - async page(offset: number, count: number): Promise { - const searchResults = await this.callSearch(offset, count) - return extractEntitiesFromSearchResults(this.schema, searchResults) - } - - /** - * Returns a page of entity IDs that match this query. - * @param offset The offset for where to start returning entity IDs. - * @param count The number of entity IDs to return. - * @returns An array of strings matching the query. - */ - async pageOfIds(offset: number, count: number): Promise { - const searchResults = await this.callSearch(offset, count, true) - return extractEntityIdsFromSearchResults(this.schema, searchResults) - } - - /** - * Returns a page of key names in Redis that match this query. - * @param offset The offset for where to start returning key names. - * @param count The number of key names to return. - * @returns An array of strings matching the query. - */ - async pageOfKeys(offset: number, count: number): Promise { - const searchResults = await this.callSearch(offset, count, true) - return extractKeyNamesFromSearchResults(searchResults) - } - - /** - * Returns the first {@link Entity} that matches this query. - */ - async first(): Promise { - const foundEntity = await this.page(0, 1) - return foundEntity[0] ?? null - } - - /** - * Returns the first entity ID that matches this query. - */ - async firstId(): Promise { - const foundIds = await this.pageOfIds(0, 1) - return foundIds[0] ?? null - } - - /** - * Returns the first key name that matches this query. - */ - async firstKey(): Promise { - const foundKeys = await this.pageOfKeys(0, 1) - return foundKeys[0] ?? null - } - - /** - * Returns all the {@link Entity | Entities} that match this query. This method - * makes multiple calls to Redis until all the {@link Entity | Entities} are returned. - * You can specify the batch size by setting the `pageSize` property on the - * options: - * - * ```typescript - * const entities = await repository.search().returnAll({ pageSize: 100 }) - * ``` - * - * @param options Options for the call. - * @param options.pageSize Number of {@link Entity | Entities} returned per batch. - * @returns An array of {@link Entity | Entities} matching the query. - */ - async all(options = { pageSize: 10 }): Promise { - return this.allThings(this.page, options) as Promise - } - - /** - * Returns all the entity IDs that match this query. This method - * makes multiple calls to Redis until all the entity IDs are returned. - * You can specify the batch size by setting the `pageSize` property on the - * options: - * - * ```typescript - * const keys = await repository.search().returnAllIds({ pageSize: 100 }) - * ``` - * - * @param options Options for the call. - * @param options.pageSize Number of entity IDs returned per batch. - * @returns An array of entity IDs matching the query. - */ - async allIds(options = { pageSize: 10 }): Promise { - return this.allThings(this.pageOfIds, options) as Promise - } - - /** - * Returns all the key names in Redis that match this query. This method - * makes multiple calls to Redis until all the key names are returned. - * You can specify the batch size by setting the `pageSize` property on the - * options: - * - * ```typescript - * const keys = await repository.search().returnAllKeys({ pageSize: 100 }) - * ``` - * - * @param options Options for the call. - * @param options.pageSize Number of key names returned per batch. - * @returns An array of key names matching the query. - */ - async allKeys(options = { pageSize: 10 }): Promise { - return this.allThings(this.pageOfKeys, options) as Promise - } - - /** - * Returns the current instance. Syntactic sugar to make your code more fluent. - * @returns this - */ - get return(): AbstractSearch { - return this - } - - /** - * Alias for {@link Search.min}. - */ - async returnMin(field: string): Promise { - return await this.min(field) - } - - /** - * Alias for {@link Search.minId}. - */ - async returnMinId(field: string): Promise { - return await this.minId(field) - } - - /** - * Alias for {@link Search.minKey}. - */ - async returnMinKey(field: string): Promise { - return await this.minKey(field) - } - - /** - * Alias for {@link Search.max}. - */ - async returnMax(field: string): Promise { - return await this.max(field) - } - - /** - * Alias for {@link Search.maxId}. - */ - async returnMaxId(field: string): Promise { - return await this.maxId(field) - } - - /** - * Alias for {@link Search.maxKey}. - */ - async returnMaxKey(field: string): Promise { - return await this.maxKey(field) - } - - /** - * Alias for {@link Search.count}. - */ - async returnCount(): Promise { - return await this.count() - } - - /** - * Alias for {@link Search.page}. - */ - async returnPage(offset: number, count: number): Promise { - return await this.page(offset, count) - } - - /** - * Alias for {@link Search.pageOfIds}. - */ - async returnPageOfIds(offset: number, count: number): Promise { - return await this.pageOfIds(offset, count) - } - - /** - * Alias for {@link Search.pageOfKeys}. - */ - async returnPageOfKeys(offset: number, count: number): Promise { - return await this.pageOfKeys(offset, count) - } - - /** - * Alias for {@link Search.first}. - */ - async returnFirst(): Promise { - return await this.first() - } - - /** - * Alias for {@link Search.firstId}. - */ - async returnFirstId(): Promise { - return await this.firstId() - } - - /** - * Alias for {@link Search.firstKey}. - */ - async returnFirstKey(): Promise { - return await this.firstKey() - } - - /** - * Alias for {@link Search.all}. - */ - async returnAll(options = { pageSize: 10 }): Promise { - return await this.all(options) - } - - /** - * Alias for {@link Search.allIds}. - */ - async returnAllIds(options = { pageSize: 10 }): Promise { - return await this.allIds(options) - } - - /** - * Alias for {@link Search.allKeys}. - */ - async returnAllKeys(options = { pageSize: 10 }): Promise { - return await this.allKeys(options) - } - - private async allThings(pageFn: Function, options = { pageSize: 10 }): Promise { - const things = [] - let offset = 0 - const pageSize = options.pageSize - - while (true) { - const foundThings = await pageFn.call(this, offset, pageSize) - things.push(...foundThings) - if (foundThings.length < pageSize) break - offset += pageSize - } - - return things - } - - private async callSearch(offset = 0, count = 0, keysOnly = false) { - - const dataStructure = this.schema.dataStructure - const indexName = this.schema.indexName - const query = this.query - const options: SearchOptions = { - LIMIT: { from: offset, size: count } - } - - if (this.sortOptions !== undefined) options.SORTBY = this.sortOptions - - if (keysOnly) { - options.RETURN = [] - } else if (dataStructure === 'JSON') { - options.RETURN = '$' - } - - let searchResults - try { - searchResults = await this.client.search(indexName, query, options) - } catch (error) { - const message = (error as Error).message - if (message.startsWith("Syntax error")) { - throw new SearchError(`The query to RediSearch had a syntax error: "${message}".\nThis is often the result of using a stop word in the query. Either change the query to not use a stop word or change the stop words in the schema definition. You can check the RediSearch source for the default stop words at: https://github.com/RediSearch/RediSearch/blob/master/src/stopwords.h.`) - } - throw error - } - return searchResults - } -} - -/** - * Entry point to raw search which allows using raw RediSearch queries - * against Redis OM. Requires that RediSearch (and optionally RedisJSON) be - * installed. - * @template TEntity The type of {@link Entity} being sought. - */ -export class RawSearch extends AbstractSearch { - private rawQuery: string - - /** @internal */ - constructor(schema: Schema, client: Client, query: string = '*') { - super(schema, client) - this.rawQuery = query - } - - /** @internal */ - get query(): string { - return this.rawQuery - } -} - - -/** - * Entry point to fluent search. This is the default Redis OM experience. - * Requires that RediSearch (and optionally RedisJSON) be installed. - * @template TEntity The type of {@link Entity} being sought. - */ -export class Search extends AbstractSearch { - private rootWhere?: Where - - /** @internal */ - get query(): string { - if (this.rootWhere === undefined) return '*' - return `${this.rootWhere.toString()}` - } - - /** - * Sets up a query matching a particular field. If there are multiple calls - * to {@link Search.where}, they are treated logically as AND. - * @param field The field to filter on. - * @returns A subclass of {@link WhereField} matching the type of the field. - */ - where(field: string): WhereField - - /** - * Sets up a nested search. If there are multiple calls to {@link Search.where}, - * they are treated logically as AND. - * @param subSearchFn A function that takes a {@link Search} and returns another {@link Search}. - * @returns `this`. - */ - where(subSearchFn: SubSearchFunction): Search - where(fieldOrFn: string | SubSearchFunction): WhereField | Search { - return this.anyWhere(WhereAnd, fieldOrFn) - } - - /** - * Sets up a query matching a particular field as a logical AND. - * @param field The field to filter on. - * @returns A subclass of {@link WhereField} matching the type of the field. - */ - and(field: string): WhereField - - /** - * Sets up a nested search as a logical AND. - * @param subSearchFn A function that takes a {@link Search} and returns another {@link Search}. - * @returns `this`. - */ - and(subSearchFn: SubSearchFunction): Search - and(fieldOrFn: string | SubSearchFunction): WhereField | Search { - return this.anyWhere(WhereAnd, fieldOrFn) - } - - /** - * Sets up a query matching a particular field as a logical OR. - * @param field The field to filter on. - * @returns A subclass of {@link WhereField} matching the type of the field. - */ - or(field: string): WhereField - - /** - * Sets up a nested search as a logical OR. - * @param subSearchFn A function that takes a {@link Search} and returns another {@link Search}. - * @returns `this`. - */ - or(subSearchFn: SubSearchFunction): Search - or(fieldOrFn: string | SubSearchFunction): WhereField | Search { - return this.anyWhere(WhereOr, fieldOrFn) - } - - private anyWhere(ctor: AndOrConstructor, fieldOrFn: string | SubSearchFunction): WhereField | Search { - if (typeof fieldOrFn === 'string') { - return this.anyWhereForField(ctor, fieldOrFn) - } else { - return this.anyWhereForFunction(ctor, fieldOrFn) - } - } - - private anyWhereForField(ctor: AndOrConstructor, field: string): WhereField { - const where = this.createWhere(field) - - if (this.rootWhere === undefined) { - this.rootWhere = where - } else { - this.rootWhere = new ctor(this.rootWhere, where) - } - - return where - } - - private anyWhereForFunction(ctor: AndOrConstructor, subSearchFn: SubSearchFunction): Search { - const search = new Search(this.schema, this.client) - const subSearch = subSearchFn(search) - - if (subSearch.rootWhere === undefined) { - throw new SearchError("Sub-search without a root where was somehow defined.") - } else { - if (this.rootWhere === undefined) { - this.rootWhere = subSearch.rootWhere - } else { - this.rootWhere = new ctor(this.rootWhere, subSearch.rootWhere) - } - } - - return this - } - - private createWhere(fieldName: string): WhereField { - const field = this.schema.fieldByName(fieldName) - - if (field === null) throw new FieldNotInSchema(fieldName) - - if (field.type === 'boolean' && this.schema.dataStructure === 'HASH') return new WhereHashBoolean(this, field) - if (field.type === 'boolean' && this.schema.dataStructure === 'JSON') return new WhereJsonBoolean(this, field) - if (field.type === 'date') return new WhereDate(this, field) - if (field.type === 'number') return new WhereNumber(this, field) - if (field.type === 'point') return new WherePoint(this, field) - if (field.type === 'text') return new WhereText(this, field) - if (field.type === 'string') return new WhereString(this, field) - if (field.type === 'string[]') return new WhereStringArray(this, field) - - // @ts-ignore: This is a trap for JavaScript - throw new RedisOmError(`The field type of '${fieldDef.type}' is not a valid field type. Valid types include 'boolean', 'date', 'number', 'point', 'string', and 'string[]'.`) - } -} diff --git a/lib/search/where-and.ts b/lib/search/where-and.ts deleted file mode 100644 index e663299..0000000 --- a/lib/search/where-and.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Where } from "./where" - -export class WhereAnd extends Where { - private left: Where - private right: Where - - constructor(left: Where, right: Where) { - super() - this.left = left - this.right = right - } - - toString(): string { - return `( ${this.left.toString()} ${this.right.toString()} )` - } -} diff --git a/lib/search/where-boolean.ts b/lib/search/where-boolean.ts deleted file mode 100644 index ea58b80..0000000 --- a/lib/search/where-boolean.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { Search } from "./search" -import { WhereField } from "./where-field" - -export abstract class WhereBoolean extends WhereField { - protected value!: boolean - - eq(value: boolean): Search { - this.value = value - return this.search - } - - equal(value: boolean): Search { return this.eq(value) } - equals(value: boolean): Search { return this.eq(value) } - equalTo(value: boolean): Search { return this.eq(value) } - - true(): Search { return this.eq(true) } - false(): Search { return this.eq(false) } - - abstract toString(): string -} - -export class WhereHashBoolean extends WhereBoolean { - toString(): string { - return this.buildQuery(`{${this.value ? '1' : '0'}}`) - } -} - -export class WhereJsonBoolean extends WhereBoolean { - toString(): string { - return this.buildQuery(`{${this.value}}`) - } -} diff --git a/lib/search/where-date.ts b/lib/search/where-date.ts deleted file mode 100644 index ad1b547..0000000 --- a/lib/search/where-date.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { Search } from "./search" -import { WhereField } from "./where-field" - -export class WhereDate extends WhereField { - private lower: number = Number.NEGATIVE_INFINITY - private upper: number = Number.POSITIVE_INFINITY - private lowerExclusive: boolean = false - private upperExclusive: boolean = false - - eq(value: Date | number | string): Search { - const epoch = this.coerceDateToEpoch(value) - this.lower = epoch - this.upper = epoch - return this.search - } - - gt(value: Date | number | string): Search { - const epoch = this.coerceDateToEpoch(value) - this.lower = epoch - this.lowerExclusive = true - return this.search - } - - gte(value: Date | number | string): Search { - this.lower = this.coerceDateToEpoch(value) - return this.search - } - - lt(value: Date | number | string): Search { - this.upper = this.coerceDateToEpoch(value) - this.upperExclusive = true - return this.search - } - - lte(value: Date | number | string): Search { - this.upper = this.coerceDateToEpoch(value) - return this.search - } - - between(lower: Date | number | string, upper: Date | number | string): Search { - this.lower = this.coerceDateToEpoch(lower) - this.upper = this.coerceDateToEpoch(upper) - return this.search - } - - equal(value: Date | number | string): Search { return this.eq(value) } - equals(value: Date | number | string): Search { return this.eq(value) } - equalTo(value: Date | number | string): Search { return this.eq(value) } - - greaterThan(value: Date | number | string): Search { return this.gt(value) } - greaterThanOrEqualTo(value: Date | number | string): Search { return this.gte(value) } - lessThan(value: Date | number | string): Search { return this.lt(value) } - lessThanOrEqualTo(value: Date | number | string): Search { return this.lte(value) } - - on(value: Date | number | string): Search { return this.eq(value) } - after(value: Date | number | string): Search { return this.gt(value) } - before(value: Date | number | string): Search { return this.lt(value) } - onOrAfter(value: Date | number | string): Search { return this.gte(value) } - onOrBefore(value: Date | number | string): Search { return this.lte(value) } - - toString(): string { - const lower = this.makeLowerString() - const upper = this.makeUpperString() - return this.buildQuery(`[${lower} ${upper}]`) - } - - private makeLowerString() { - if (this.lower === Number.NEGATIVE_INFINITY) return '-inf' - if (this.lowerExclusive) return `(${this.lower}` - return this.lower.toString() - } - - private makeUpperString() { - if (this.upper === Number.POSITIVE_INFINITY) return '+inf' - if (this.upperExclusive) return `(${this.upper}` - return this.upper.toString() - } - - private coerceDateToEpoch(value: Date | number | string) { - if (value instanceof Date) return value.getTime() / 1000 - if (typeof value === 'string') return new Date(value).getTime() / 1000 - return value - } -} diff --git a/lib/search/where-field.ts b/lib/search/where-field.ts deleted file mode 100644 index 9f2188f..0000000 --- a/lib/search/where-field.ts +++ /dev/null @@ -1,316 +0,0 @@ -import { Field } from "../schema" -import { Search } from "./search" -import { Where } from "./where" -import { CircleFunction } from "./where-point" - -/** - * Interface with all the methods from all the concrete - * classes under {@link WhereField}. - */ -export interface WhereField extends Where { - - /** - * Adds an equals comparison to the query. - * - * NOTE: this function is not available for strings where full-text - * search is enabled. In that scenario, use `.match`. - * @param value The value to be compared - * @returns The {@link Search} that was called to create this {@link WhereField}. - */ - eq(value: string | number | boolean | Date): Search - - /** - * Adds an equals comparison to the query. - * - * NOTE: this function is not available for strings where full-text - * search is enabled. In that scenario, use `.match`. - * @param value The value to be compared - * @returns The {@link Search} that was called to create this {@link WhereField}. - */ - equal(value: string | number | boolean | Date): Search - - /** - * Adds an equals comparison to the query. - * - * NOTE: this function is not available for strings where full-text - * search is enabled. In that scenario, use `.match`. - * @param value The value to be compared - * @returns The {@link Search} that was called to create this {@link WhereField}. - */ - equals(value: string | number | boolean | Date): Search - - /** - * Adds an equals comparison to the query. - * - * NOTE: this function is not available for strings where full-text - * search is enabled. In that scenario, use `.match`. - * @param value The value to be compared - * @returns The {@link Search} that was called to create this {@link WhereField}. - */ - equalTo(value: string | number | boolean | Date): Search - - /** - * Adds a full-text search comparison to the query. - * @param value The word or phrase sought. - * @returns The {@link Search} that was called to create this {@link WhereField}. - */ - match(value: string | number | boolean): Search - - /** - * Adds a full-text search comparison to the query. - * @param value The word or phrase sought. - * @returns The {@link Search} that was called to create this {@link WhereField}. - */ - matches(value: string | number | boolean): Search - - /** - * Adds a full-text search comparison to the query that matches an exact word or phrase. - * @param value The word or phrase sought. - * @returns The {@link Search} that was called to create this {@link WhereField}. - */ - matchExact(value: string | number | boolean): Search - - /** - * Adds a full-text search comparison to the query that matches an exact word or phrase. - * @param value The word or phrase sought. - * @returns The {@link Search} that was called to create this {@link WhereField}. - */ - matchExactly(value: string | number | boolean): Search - - /** - * Adds a full-text search comparison to the query that matches an exact word or phrase. - * @param value The word or phrase sought. - * @returns The {@link Search} that was called to create this {@link WhereField}. - */ - matchesExactly(value: string | number | boolean): Search - - /** - * Makes a call to {@link WhereField.match} a {@link WhereField.matchExact} call. Calling - * this multiple times will have no effect. - * @returns this. - */ - readonly exact: WhereField - - /** - * Makes a call to {@link WhereField.match} a {@link WhereField.matchExact} call. Calling - * this multiple times will have no effect. - * @returns this. - */ - readonly exactly: WhereField - - /** - * Adds a boolean match with a value of `true` to the query. - * @returns The {@link Search} that was called to create this {@link WhereField}. - */ - true(): Search - - /** - * Adds a boolean match with a value of `false` to the query. - * @returns The {@link Search} that was called to create this {@link WhereField}. - */ - false(): Search - - /** - * Adds a greater than comparison against a field to the search query. - * @param value The value to compare against. - * @returns The {@link Search} that was called to create this {@link WhereField}. - */ - gt(value: string | number | Date): Search - - /** - * Adds a greater than comparison against a field to the search query. - * @param value The value to compare against. - * @returns The {@link Search} that was called to create this {@link WhereField}. - */ - greaterThan(value: string | number | Date): Search - - /** - * Adds a greater than or equal to comparison against a field to the search query. - * @param value The value to compare against. - * @returns The {@link Search} that was called to create this {@link WhereField}. - */ - gte(value: string | number | Date): Search - - /** - * Adds a greater than or equal to comparison against a field to the search query. - * @param value The value to compare against. - * @returns The {@link Search} that was called to create this {@link WhereField}. - */ - greaterThanOrEqualTo(value: string | number | Date): Search - - /** - * Adds a less than comparison against a field to the search query. - * @param value The value to compare against. - * @returns The {@link Search} that was called to create this {@link WhereField}. - */ - lt(value: string | number | Date): Search - - /** - * Adds a less than comparison against a field to the search query. - * @param value The value to compare against. - * @returns The {@link Search} that was called to create this {@link WhereField}. - */ - lessThan(value: string | number | Date): Search - - /** - * Adds a less than or equal to comparison against a field to the search query. - * @param value The value to compare against. - * @returns The {@link Search} that was called to create this {@link WhereField}. - */ - lte(value: string | number | Date): Search - - /** - * Adds a less than or equal to comparison against a field to the search query. - * @param value The value to compare against. - * @returns The {@link Search} that was called to create this {@link WhereField}. - */ - lessThanOrEqualTo(value: string | number | Date): Search - - /** - * Adds an inclusive range comparison against a field to the search query. - * @param lower The lower bound of the range. - * @param upper The upper bound of the range. - * @returns The {@link Search} that was called to create this {@link WhereField}. - */ - between(lower: string | number | Date, upper: string | number | Date): Search - - /** - * Adds a whole-string match for a value within a string array to the search query. - * @param value The string to be matched. - * @returns The {@link Search} that was called to create this {@link WhereField}. - */ - contain(value: string): Search - - /** - * Adds a whole-string match for a value within a string array to the search query. - * @param value The string to be matched. - * @returns The {@link Search} that was called to create this {@link WhereField}. - */ - contains(value: string): Search - - /** - * Adds a whole-string match against a string array to the query. If any of the provided - * strings in `value` is matched in the array, this matched. - * @param value An array of strings that you want to match one of. - * @returns The {@link Search} that was called to create this {@link WhereField}. - */ - containOneOf(...value: Array): Search - - /** - * Adds a whole-string match against a string array to the query. If any of the provided - * strings in `value` is matched in the array, this matched. - * @param value An array of strings that you want to match one of. - * @returns The {@link Search} that was called to create this {@link WhereField}. - */ - containsOneOf(...value: Array): Search - - /** - * Adds a search for points that fall within a defined circle. - * @param circleFn A function that returns a {@link Circle} instance defining the search area. - * @returns The {@link Search} that was called to create this {@link WhereField}. - */ - inCircle(circleFn: CircleFunction): Search - - /** - * Adds a search for points that fall within a defined radius. - * @param circleFn A function that returns a {@link Circle} instance defining the search area. - * @returns The {@link Search} that was called to create this {@link WhereField}. - */ - inRadius(circleFn: CircleFunction): Search - - /** - * Add a search for an exact UTC datetime to the query. - * @param value The datetime to match. - * @returns The {@link Search} that was called to create this {@link WhereField}. - */ - on(value: string | number | Date): Search - - /** - * Add a search that matches all datetimes *before* the provided UTC datetime to the query. - * @param value The datetime to compare against. - * @returns The {@link Search} that was called to create this {@link WhereField}. - */ - before(value: string | number | Date): Search - - /** - * Add a search that matches all datetimes *after* the provided UTC datetime to the query. - * @param value The datetime to compare against. - * @returns The {@link Search} that was called to create this {@link WhereField}. - */ - after(value: string | number | Date): Search - - /** - * Add a search that matches all datetimes *on or before* the provided UTC datetime to the query. - * @param value The datetime to compare against. - * @returns The {@link Search} that was called to create this {@link WhereField}. - */ - onOrBefore(value: string | number | Date): Search - - /** - * Add a search that matches all datetimes *on or after* the provided UTC datetime to the query. - * @param value The datetime to compare against. - * @returns The {@link Search} that was called to create this {@link WhereField}. - */ - onOrAfter(value: string | number | Date): Search -} - -/** - * Abstract base class that all fields you want to filter - * with extend. When you call {@link Search.where}, a - * subclass of this is returned. - */ -export abstract class WhereField { - private negated: boolean = false - - /** @internal */ - protected search: Search - - /** @internal */ - protected field: Field - - /** @internal */ - constructor(search: Search, field: Field) { - this.search = search - this.field = field - } - - /** - * Returns the current instance. Syntactic sugar to make your code more fluent. - * @returns this - */ - get is() { - return this - } - - /** - * Returns the current instance. Syntactic sugar to make your code more fluent. - * @returns this - */ - get does() { - return this - } - - /** - * Negates the query on the field, cause it to match when the condition is - * *not* met. Calling this multiple times will negate the negation. - * @returns this - */ - get not() { - this.negate() - return this - } - - abstract toString(): string - - /** @internal */ - protected negate() { - this.negated = !this.negated - } - - /** @internal */ - protected buildQuery(valuePortion: string): string { - const negationPortion = this.negated ? '-' : '' - const fieldPortion = this.field.name - return `(${negationPortion}@${fieldPortion}:${valuePortion})` - } -} diff --git a/lib/search/where-number.ts b/lib/search/where-number.ts deleted file mode 100644 index cabe670..0000000 --- a/lib/search/where-number.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { Search } from "./search" -import { WhereField } from "./where-field" - -export class WhereNumber extends WhereField { - private lower: number = Number.NEGATIVE_INFINITY - private upper: number = Number.POSITIVE_INFINITY - private lowerExclusive: boolean = false - private upperExclusive: boolean = false - - eq(value: number): Search { - this.lower = value - this.upper = value - return this.search - } - - gt(value: number): Search { - this.lower = value - this.lowerExclusive = true - return this.search - } - - gte(value: number): Search { - this.lower = value - return this.search - } - - lt(value: number): Search { - this.upper = value - this.upperExclusive = true - return this.search - } - - lte(value: number): Search { - this.upper = value - return this.search - } - - between(lower: number, upper: number): Search { - this.lower = lower - this.upper = upper - return this.search - } - - equal(value: number): Search { return this.eq(value) } - equals(value: number): Search { return this.eq(value) } - equalTo(value: number): Search { return this.eq(value) } - - greaterThan(value: number): Search { return this.gt(value) } - greaterThanOrEqualTo(value: number): Search { return this.gte(value) } - lessThan(value: number): Search { return this.lt(value) } - lessThanOrEqualTo(value: number): Search { return this.lte(value) } - - toString(): string { - const lower = this.makeLowerString() - const upper = this.makeUpperString() - return this.buildQuery(`[${lower} ${upper}]`) - } - - private makeLowerString() { - if (this.lower === Number.NEGATIVE_INFINITY) return '-inf' - if (this.lowerExclusive) return `(${this.lower}` - return this.lower.toString() - } - - private makeUpperString() { - if (this.upper === Number.POSITIVE_INFINITY) return '+inf' - if (this.upperExclusive) return `(${this.upper}` - return this.upper.toString() - } -} diff --git a/lib/search/where-or.ts b/lib/search/where-or.ts deleted file mode 100644 index 13f8237..0000000 --- a/lib/search/where-or.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Where } from "./where" - -export class WhereOr extends Where { - private left: Where - private right: Where - - constructor(left: Where, right: Where) { - super() - this.left = left - this.right = right - } - - toString(): string { - return `( ${this.left.toString()} | ${this.right.toString()} )` - } -} diff --git a/lib/search/where-point.ts b/lib/search/where-point.ts deleted file mode 100644 index 76ba1ae..0000000 --- a/lib/search/where-point.ts +++ /dev/null @@ -1,192 +0,0 @@ -import { Point } from "../entity" -import { Search } from "./search" -import { WhereField } from "./where-field" - -type Units = 'm' | 'km' | 'ft' | 'mi' - -/** A function that defines a circle for `.inCircle` searches. */ -export type CircleFunction = (circle: Circle) => Circle - -/** A builder that defines a circle. */ -export class Circle { - - /** @internal */ - longitudeOfOrigin: number = 0 - - /** @internal */ - latitudeOfOrigin: number = 0 - - /** @internal */ - size: number = 1 - - /** @internal */ - units: Units = 'm' - - /** - * Sets the longitude. If not set, defaults to 0.0. - * - * @param value The longitude. - * @returns This instance. - */ - longitude(value: number) { - this.longitudeOfOrigin = value - return this - } - - /** - * Sets the latitude. If not set, defaults to 0.0. - * - * @param value The latitude. - * @returns This instance. - */ - latitude(value: number) { - this.latitudeOfOrigin = value - return this - } - - /** - * Sets the origin of the circle using a {@link Point}. If not - * set, defaults to [Null Island](https://en.wikipedia.org/wiki/Null_Island). - * - * @param point A {@link Point} containing the longitude and latitude of the origin. - * @returns This instance. - */ - origin(point: Point): Circle - - /** - * Sets the origin of the circle. If not set, defaults to - * [Null Island](https://en.wikipedia.org/wiki/Null_Island). - * - * @param longitude The longitude. - * @param latitude The latitude. - * @returns This instance. - */ - origin(longitude: number, latitude: number): Circle - - /** @internal */ - origin(pointOrLongitude: number | Point, latitude?: number): Circle { - if (typeof pointOrLongitude === 'number' && latitude !== undefined) { - this.longitudeOfOrigin = pointOrLongitude - this.latitudeOfOrigin = latitude - } else { - const point = pointOrLongitude as Point - this.longitudeOfOrigin = point.longitude - this.latitudeOfOrigin = point.latitude - } - return this - } - - /** - * Sets the radius of the {@link Circle}. Defaults to 1. If units are - * not specified, defaults to meters. - * - * @param size The radius of the circle. - * @returns This instance. - */ - radius(size: number) { - this.size = size - return this - } - - /** - * Sets the units to meters. - * @returns This instance. - */ - get m() { return this.meters } - - /** - * Sets the units to meters. - * @returns This instance. - */ - get meter() { return this.meters } - - /** - * Sets the units to meters. - * @returns This instance. - */ - get meters() { - this.units = 'm' - return this - } - - /** - * Sets the units to kilometers. - * @returns This instance. - */ - get km() { return this.kilometers } - - /** - * Sets the units to kilometers. - * @returns This instance. - */ - get kilometer() { return this.kilometers } - - /** - * Sets the units to kilometers. - * @returns This instance. - */ - get kilometers() { - this.units = 'km' - return this - } - - /** - * Sets the units to feet. - * @returns This instance. - */ - get ft() { return this.feet } - - /** - * Sets the units to feet. - * @returns This instance. - */ - get foot() { return this.feet } - - /** - * Sets the units to feet. - * @returns This instance. - */ - get feet() { - this.units = 'ft' - return this - } - - /** - * Sets the units to miles. - * @returns This instance. - */ - get mi() { return this.miles } - - /** - * Sets the units to miles. - * @returns This instance. - */ - get mile() { return this.miles } - - /** - * Sets the units to miles. - * @returns This instance. - */ - get miles() { - this.units = 'mi' - return this - } -} - -export class WherePoint extends WhereField { - private circle: Circle = new Circle() - - inRadius(circleFn: CircleFunction): Search { - return this.inCircle(circleFn) - } - - inCircle(circleFn: CircleFunction): Search { - this.circle = circleFn(this.circle) - return this.search - } - - toString(): string { - const { longitudeOfOrigin, latitudeOfOrigin, size, units } = this.circle - return this.buildQuery(`[${longitudeOfOrigin} ${latitudeOfOrigin} ${size} ${units}]`) - } -} diff --git a/lib/search/where-string-array.ts b/lib/search/where-string-array.ts deleted file mode 100644 index f5d1804..0000000 --- a/lib/search/where-string-array.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Search } from "./search" -import { WhereField } from "./where-field" - -export class WhereStringArray extends WhereField { - private value!: Array - - contain(value: string): Search { - this.value = [value] - return this.search - } - - contains(value: string): Search { return this.contain(value) } - - containsOneOf(...value: Array): Search { - this.value = value - return this.search - } - - containOneOf(...value: Array): Search { return this.containsOneOf(...value) } - - toString(): string { - const matchPunctuation = /[,.<>{}[\]"':;!@#$%^&()\-+=~| ]/g - const escapedValue = this.value.map(s => s.replace(matchPunctuation, '\\$&')).join('|') - return this.buildQuery(`{${escapedValue}}`) - } -} diff --git a/lib/search/where-string.ts b/lib/search/where-string.ts deleted file mode 100644 index cf85b86..0000000 --- a/lib/search/where-string.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { Search } from "./search" -import { WhereField } from "./where-field" -import { SemanticSearchError } from "../error" - -export class WhereString extends WhereField { - private value!: string - - eq(value: string | number | boolean): Search { - this.value = value.toString() - return this.search - } - - equal(value: string | number | boolean): Search { return this.eq(value) } - equals(value: string | number | boolean): Search { return this.eq(value) } - equalTo(value: string | number | boolean): Search { return this.eq(value) } - - match(_: string | number | boolean): Search { return this.throwMatchExcpetion() } - matches(_: string | number | boolean): Search { return this.throwMatchExcpetion() } - matchExact(_: string | number | boolean): Search { return this.throwMatchExcpetion() } - matchExactly(_: string | number | boolean): Search { return this.throwMatchExcpetion() } - matchesExactly(_: string | number | boolean): Search { return this.throwMatchExcpetion() } - - get exact() { return this.throwMatchExcpetionReturningThis() } - get exactly() { return this.throwMatchExcpetionReturningThis() } - - toString(): string { - const matchPunctuation = /[,.<>{}[\]"':;!@#$%^&()\-+=~|/\\ ]/g - const escapedValue = this.value.replace(matchPunctuation, '\\$&') - return this.buildQuery(`{${escapedValue}}`) - } - - private throwMatchExcpetion(): Search { - throw new SemanticSearchError("Cannot perform full-text search operations like .match on field of type 'string'. If full-text search is needed on this field, change the type to 'text' in the Schema.") - } - - private throwMatchExcpetionReturningThis(): WhereString { - throw new SemanticSearchError("Cannot perform full-text search operations like .match on field of type 'string'. If full-text search is needed on this field, change the type to 'text' in the Schema.") - } -} diff --git a/lib/search/where-text.ts b/lib/search/where-text.ts deleted file mode 100644 index ac818d7..0000000 --- a/lib/search/where-text.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { Search } from "./search" -import { WhereField } from "./where-field" - -import { SemanticSearchError } from "../error" - -export class WhereText extends WhereField { - private value!: string - private exactValue = false - - match(value: string | number | boolean): Search { - this.value = value.toString() - return this.search - } - - matchExact(value: string | number | boolean): Search { - this.exact.value = value.toString() - return this.search - } - - matches(value: string | number | boolean): Search { return this.match(value) } - matchExactly(value: string | number | boolean): Search { return this.matchExact(value) } - matchesExactly(value: string | number | boolean): Search { return this.matchExact(value) } - - get exact() { - this.exactValue = true - return this - } - - get exactly() { - return this.exact - } - - eq(_: string | number | boolean): Search { return this.throwEqualsExcpetion() } - equal(_: string | number | boolean): Search { return this.throwEqualsExcpetion() } - equals(_: string | number | boolean): Search { return this.throwEqualsExcpetion() } - equalTo(_: string | number | boolean): Search { return this.throwEqualsExcpetion() } - - toString(): string { - const matchPunctuation = /[,.<>{}[\]"':;!@#$%^&()\-+=~|]/g - const escapedValue = this.value.replace(matchPunctuation, '\\$&') - - if (this.exactValue) { - return this.buildQuery(`"${escapedValue}"`) - } else { - return this.buildQuery(`'${escapedValue}'`) - } - } - - private throwEqualsExcpetion(): Search { - throw new SemanticSearchError("Cannot call .equals on a field of type 'text', either use .match to perform full-text search or change the type to 'string' in the Schema.") - } -} diff --git a/lib/search/where.ts b/lib/search/where.ts deleted file mode 100644 index c505a17..0000000 --- a/lib/search/where.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Abstract base class used extensively with {@link Search}. - */ -export abstract class Where { - /** - * Converts this {@link Where} into a portion of a RediSearch query. - */ - abstract toString(): string -} diff --git a/lib/transformer/from-hash-transformer.ts b/lib/transformer/from-hash-transformer.ts deleted file mode 100644 index 9f50d98..0000000 --- a/lib/transformer/from-hash-transformer.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { Field, Schema } from "../schema" -import { RedisHashData } from "../client" -import { convertEpochStringToDate, convertStringToNumber, convertStringToPoint, isNotNullish, isNumberString, isPointString, stringifyError } from "./transformer-common" -import { EntityData } from "../entity" -import { InvalidHashValue } from "../error" - -export function fromRedisHash(schema: Schema, hashData: RedisHashData): EntityData { - const data: { [key: string]: any } = { ...hashData } - schema.fields.forEach(field => { - if (field.hashField) delete data[field.hashField] - const value = hashData[field.hashField] - if (isNotNullish(value)) data[field.name] = convertKnownValueFromString(field, value!) - }) - return data -} - -function convertKnownValueFromString(field: Field, value: string): any { - switch (field.type) { - case 'boolean': - if (value === '1') return true - if (value === '0') return false - throw new InvalidHashValue(field) - case 'number': - if (isNumberString(value)) return convertStringToNumber(value) - throw new InvalidHashValue(field) - case 'date': - if (isNumberString(value)) return convertEpochStringToDate(value) - throw new InvalidHashValue(field) - case 'point': - if (isPointString(value)) return convertStringToPoint(value) - throw new InvalidHashValue(field) - case 'string': - case 'text': - return value - case 'string[]': - return convertStringToStringArray(value, field.separator) - } -} - -const convertStringToStringArray = (value: string, separator: string): string[] => value.split(separator) diff --git a/lib/transformer/from-json-transformer.ts b/lib/transformer/from-json-transformer.ts deleted file mode 100644 index cf03f10..0000000 --- a/lib/transformer/from-json-transformer.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { JSONPath } from 'jsonpath-plus' -import clone from 'just-clone' - -import { Field, Schema } from "../schema" -import { RedisJsonData } from "../client" - -import { convertEpochToDate, convertKnownValueToString, convertStringToPoint, isArray, isBoolean, isNull, isNumber, isPointString, isString, stringifyError } from "./transformer-common" -import { EntityData } from '../entity' -import { InvalidJsonValue, NullJsonValue } from '../error' - - -export function fromRedisJson(schema: Schema, json: RedisJsonData): EntityData { - const data: EntityData = clone(json) - convertFromRedisJsonKnown(schema, data) - return data -} - -function convertFromRedisJsonKnown(schema: Schema, data: EntityData) { - schema.fields.forEach(field => { - - const path = field.jsonPath - const results = JSONPath({ resultType: 'all', path, json: data }) - - if (field.type === 'string[]') { - results.forEach((result: any) => { - const { value, parent, parentProperty } = result - if (isNull(value)) throw new NullJsonValue(field) - parent[parentProperty] = convertKnownValueToString(value) - }) - } else if (results.length === 1) { - const [ { value, parent, parentProperty } ] = results - parent[parentProperty] = convertKnownValueFromJson(field, value) - } else if (results.length > 1) { - throw new InvalidJsonValue(field) - } - }) -} - -function convertKnownValueFromJson(field: Field, value: any): any { - if (isNull(value)) return value - - switch (field.type) { - case 'boolean': - if (isBoolean(value)) return value - throw new InvalidJsonValue(field) - case 'number': - if (isNumber(value)) return value - throw new InvalidJsonValue(field) - case 'date': - if (isNumber(value)) return convertEpochToDate(value) - throw new InvalidJsonValue(field) - case 'point': - if (isPointString(value)) return convertStringToPoint(value) - throw new InvalidJsonValue(field) - case 'string': - case 'text': - if (isBoolean(value)) return value.toString() - if (isNumber(value)) return value.toString() - if (isString(value)) return value - throw new InvalidJsonValue(field) - case 'string[]': - if (isArray(value)) return convertFromJsonArrayToStringArray(field, value) - throw new NullJsonValue(field) - } -} - -const convertFromJsonArrayToStringArray = (field: Field, array: any[]): string[] => array.map(value => { - if (isNull(value)) throw new NullJsonValue(field) - return value.toString() -}) diff --git a/lib/transformer/index.ts b/lib/transformer/index.ts deleted file mode 100644 index 1db8d40..0000000 --- a/lib/transformer/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './from-hash-transformer' -export * from './from-json-transformer' -export * from './to-hash-transformer' -export * from './to-json-transformer' diff --git a/lib/transformer/to-hash-transformer.ts b/lib/transformer/to-hash-transformer.ts deleted file mode 100644 index 6af4408..0000000 --- a/lib/transformer/to-hash-transformer.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { Field, Schema } from "../schema" -import { RedisHashData } from "../client" -import { convertBooleanToString, convertDateToString, convertEpochDateToString, convertIsoDateToString, convertNumberToString, convertPointToString, isArray, isBoolean, isDate, isDateString, isNotNullish, isNumber, isObject, isPoint, isString, stringifyError } from "./transformer-common" -import { EntityData } from "../entity" -import { InvalidHashInput, NestedHashInput, RedisOmError, ArrayHashInput } from "../error" - -export function toRedisHash(schema: Schema, data: EntityData): RedisHashData { - const hashData: RedisHashData = {} - Object.entries(data).forEach(([key, value]) => { - if (isNotNullish(value)) { - const field = schema.fieldByName(key) - const hashField = field ? field.hashField : key - hashData[hashField] = field ? convertKnownValueToString(field, value) : convertUnknownValueToString(key, value) - } - }) - return hashData -} - -function convertKnownValueToString(field: Field, value: any): string { - switch (field.type) { - case 'boolean': - if (isBoolean(value)) return convertBooleanToString(value) - throw new InvalidHashInput(field) - case 'number': - if (isNumber(value)) return convertNumberToString(value) - throw new InvalidHashInput(field) - case 'date': - if (isNumber(value)) return convertEpochDateToString(value) - if (isDate(value)) return convertDateToString(value) - if (isDateString(value)) return convertIsoDateToString(value) - throw new InvalidHashInput(field) - case 'point': - if (isPoint(value)) return convertPointToString(value) - throw new InvalidHashInput(field) - case 'string': - case 'text': - if (isBoolean(value)) return value.toString() - if (isNumber(value)) return value.toString() - if (isString(value)) return value - throw new InvalidHashInput(field) - case 'string[]': - if (isArray(value)) return convertStringArrayToString(value, field.separator) - throw new InvalidHashInput(field) - default: - throw new RedisOmError(`Expected a valid field type but received: ${field.type}`) - } -} - -function convertUnknownValueToString(key: string, value: any): string { - if (isBoolean(value)) return convertBooleanToString(value) - if (isNumber(value)) return convertNumberToString(value) - if (isDate(value)) return convertDateToString(value) - if (isPoint(value)) return convertPointToString(value) - if (isArray(value)) throw new ArrayHashInput(key) - if (isObject(value)) throw new NestedHashInput(key) - return value.toString() -} - -const convertStringArrayToString = (value: string[], separator: string): string => value.join(separator) diff --git a/lib/transformer/to-json-transformer.ts b/lib/transformer/to-json-transformer.ts deleted file mode 100644 index 2606c7a..0000000 --- a/lib/transformer/to-json-transformer.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { JSONPath } from 'jsonpath-plus' -import clone from 'just-clone' - -import { Field, Schema } from "../schema" -import { RedisJsonData } from "../client" - -import { convertDateToEpoch, convertIsoDateToEpoch, convertKnownValueToString, convertPointToString, isArray, isBoolean, isDate, isDateString, isDefined, isNull, isNumber, isObject, isPoint, isString, isUndefined, stringifyError } from "./transformer-common" -import { EntityData } from '../entity' -import { InvalidJsonInput, NullJsonInput } from '../error' - -export function toRedisJson(schema: Schema, data: EntityData): RedisJsonData { - let json: RedisJsonData = clone(data) - convertToRedisJsonKnown(schema, json) - return convertToRedisJsonUnknown(json) -} - -function convertToRedisJsonKnown(schema: Schema, json: RedisJsonData) { - schema.fields.forEach(field => { - - const results = JSONPath({ resultType: 'all', path: field.jsonPath, json }) - - if (field.type === 'string[]') { - convertKnownResultsToJson(field, results) - return - } - - if (results.length === 0) return - - if (results.length === 1) { - convertKnownResultToJson(field, results[0]) - return - } - - throw new InvalidJsonInput(field) - }) -} - -function convertToRedisJsonUnknown(json: RedisJsonData) { - Object.entries(json).forEach(([key, value]) => { - if (isUndefined(value)) { - delete json[key] - } else if (isObject(value)) { - json[key] = convertToRedisJsonUnknown(value) - } else { - json[key] = convertUnknownValueToJson(value) - } - }) - return json -} - -function convertKnownResultToJson(field: Field, result: any): any { - const { value, parent, parentProperty } = result - if (isDefined(value)) parent[parentProperty] = convertKnownValueToJson(field, value) -} - -function convertKnownResultsToJson(field: Field, results: any[]): any { - results.forEach((result: any) => { - const { value, parent, parentProperty } = result - if (isNull(value)) throw new NullJsonInput(field) - if (isUndefined(value) && isArray(parent)) throw new NullJsonInput(field) - if (isDefined(value)) parent[parentProperty] = convertKnownValueToString(value) - }) -} - -function convertKnownValueToJson(field: Field, value: any): any { - - if (isNull(value)) return value - - switch (field.type) { - case 'boolean': - if (isBoolean(value)) return value - throw new InvalidJsonInput(field) - case 'number': - if (isNumber(value)) return value - throw new InvalidJsonInput(field) - case 'date': - if (isNumber(value)) return value - if (isDate(value)) return convertDateToEpoch(value) - if (isDateString(value)) return convertIsoDateToEpoch(value) - throw new InvalidJsonInput(field) - case 'point': - if (isPoint(value)) return convertPointToString(value) - throw new InvalidJsonInput(field) - case 'string': - case 'text': - if (isBoolean(value)) return value.toString() - if (isNumber(value)) return value.toString() - if (isString(value)) return value - throw new InvalidJsonInput(field) - } -} - -function convertUnknownValueToJson(value: any): any { - if (isDate(value)) return convertDateToEpoch(value) - return value -} diff --git a/lib/transformer/transformer-common.ts b/lib/transformer/transformer-common.ts deleted file mode 100644 index 871c315..0000000 --- a/lib/transformer/transformer-common.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { PointOutOfRange, RedisOmError } from "../error" -import { Point } from "../entity" - -export const isNull = (value: any): boolean => value === null -export const isDefined = (value: any): boolean => value !== undefined -export const isUndefined = (value: any): boolean => value === undefined -export const isNullish = (value: any): boolean => value === undefined || value === null -export const isNotNullish = (value: any): boolean => value !== undefined && value !== null -export const isBoolean = (value: any): boolean => typeof value === 'boolean' -export const isNumber = (value: any): boolean => typeof value === 'number' -export const isString = (value: any): boolean => typeof value === 'string' -export const isDate = (value: any): boolean => value instanceof Date -export const isDateString = (value: any): boolean => isString(value) && !isNaN(new Date(value).getTime()) -export const isArray = (value: any): boolean => Array.isArray(value) -export const isObject = (value: any): boolean => value !== null && typeof value === 'object' && !isArray(value) && !isDate(value) - -export const isPoint = (value: any): boolean => - isObject(value) && - Object.keys(value).length === 2 && - typeof value.latitude === 'number' && - typeof value.longitude === 'number' - -export const isNumberString = (value: string): boolean => !isNaN(Number(value)) -export const isPointString = (value: string): boolean => /^-?\d+(\.\d*)?,-?\d+(\.\d*)?$/.test(value) - -// As per https://redis.io/commands/geoadd/ and local testing -// Valid latitudes are from -85.05112878 to 85.05112878 degrees (*NOT* -90 to +90) -const isValidPoint = (value: any) => - Math.abs(value.latitude) <= 85.05112878 && - Math.abs(value.longitude) <= 180 - -export const convertBooleanToString = (value: boolean) => value ? '1' : '0' - -export const convertNumberToString = (value: number) => value.toString() -export const convertStringToNumber = (value: string): number => Number.parseFloat(value) - -export const convertDateToEpoch = (value: Date) => (value.getTime() / 1000) -export const convertDateToString = (value: Date) => convertDateToEpoch(value).toString() -export const convertEpochDateToString = (value: number) => convertNumberToString(value) - -export const convertIsoDateToEpoch = (value: string) => convertDateToEpoch(new Date(value)) -export const convertIsoDateToString = (value: string) => convertDateToString(new Date(value)) - -export const convertEpochStringToDate = (value: string): Date => new Date(convertEpochToDate(convertStringToNumber(value))) -export const convertEpochToDate = (value: number): Date => new Date(value * 1000) - -export const convertPointToString = (value: Point) => { - if (isValidPoint(value)) return `${value.longitude},${value.latitude}` - throw new PointOutOfRange(value) -} - -export const convertStringToPoint = (value: string): Point => { - const [ longitude, latitude ] = value.split(',').map(convertStringToNumber) - return { longitude: longitude!, latitude: latitude! } -} - -export function convertKnownValueToString(value: any) { - if (isBoolean(value)) return value.toString() - if (isNumber(value)) return value.toString() - if (isString(value)) return value - throw new RedisOmError(`Expected a string but received: ${stringifyError(value)}`) -} - -export const stringifyError = (value: any) => JSON.stringify(value, null, 1) diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 1524a6a..0000000 --- a/package-lock.json +++ /dev/null @@ -1,4356 +0,0 @@ -{ - "name": "redis-om", - "version": "0.4.0", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "redis-om", - "version": "0.4.0", - "license": "MIT", - "dependencies": { - "jsonpath-plus": "^7.2.0", - "just-clone": "^6.1.1", - "redis": "^4.6.4", - "ulid": "^2.3.0" - }, - "devDependencies": { - "@types/node": "^18.0.0", - "@vitest/ui": "^0.17.1", - "c8": "^7.11.3", - "tsup": "^6.1.2", - "typedoc": "^0.23.2", - "typedoc-plugin-markdown": "^3.13.1", - "typescript": "^4.7.2", - "vitest": "^0.20.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true - }, - "node_modules/@esbuild/android-arm": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.18.tgz", - "integrity": "sha512-5GT+kcs2WVGjVs7+boataCkO5Fg0y4kCjzkB5bAip7H4jfnOS3dA6KPiww9W1OEKTKeAcUVhdZGvgI65OXmUnw==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.18.tgz", - "integrity": "sha512-L4jVKS82XVhw2nvzLg/19ClLWg0y27ulRwuP7lcyL6AbUWB5aPglXY3M21mauDQMDfRLs8cQmeT03r/+X3cZYQ==", - "cpu": [ - "loong64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.17", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", - "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@polka/url": { - "version": "1.0.0-next.21", - "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.21.tgz", - "integrity": "sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==", - "dev": true - }, - "node_modules/@redis/bloom": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz", - "integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==", - "peerDependencies": { - "@redis/client": "^1.0.0" - } - }, - "node_modules/@redis/client": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.5.5.tgz", - "integrity": "sha512-fuMnpDYSjT5JXR9rrCW1YWA4L8N/9/uS4ImT3ZEC/hcaQRI1D/9FvwjriRj1UvepIgzZXthFVKMNRzP/LNL7BQ==", - "dependencies": { - "cluster-key-slot": "1.1.2", - "generic-pool": "3.9.0", - "yallist": "4.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@redis/graph": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.0.tgz", - "integrity": "sha512-16yZWngxyXPd+MJxeSr0dqh2AIOi8j9yXKcKCwVaKDbH3HTuETpDVPcLujhFYVPtYrngSco31BUcSa9TH31Gqg==", - "peerDependencies": { - "@redis/client": "^1.0.0" - } - }, - "node_modules/@redis/json": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.4.tgz", - "integrity": "sha512-LUZE2Gdrhg0Rx7AN+cZkb1e6HjoSKaeeW8rYnt89Tly13GBI5eP4CwDVr+MY8BAYfCg4/N15OUrtLoona9uSgw==", - "peerDependencies": { - "@redis/client": "^1.0.0" - } - }, - "node_modules/@redis/search": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.1.1.tgz", - "integrity": "sha512-pqCXTc5e7wJJgUuJiC3hBgfoFRoPxYzwn0BEfKgejTM7M/9zP3IpUcqcjgfp8hF+LoV8rHZzcNTz7V+pEIY7LQ==", - "peerDependencies": { - "@redis/client": "^1.0.0" - } - }, - "node_modules/@redis/time-series": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.0.4.tgz", - "integrity": "sha512-ThUIgo2U/g7cCuZavucQTQzA9g9JbDDY2f64u3AbAoz/8vE2lt2U37LamDUVChhaDA3IRT9R6VvJwqnUfTJzng==", - "peerDependencies": { - "@redis/client": "^1.0.0" - } - }, - "node_modules/@types/chai": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.4.tgz", - "integrity": "sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw==", - "dev": true - }, - "node_modules/@types/chai-subset": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@types/chai-subset/-/chai-subset-1.3.3.tgz", - "integrity": "sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==", - "dev": true, - "dependencies": { - "@types/chai": "*" - } - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", - "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", - "dev": true - }, - "node_modules/@types/node": { - "version": "18.11.18", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz", - "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==", - "dev": true - }, - "node_modules/@vitest/ui": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-0.17.1.tgz", - "integrity": "sha512-B4PGDk5IZ10HT9GzR0NQ96VEphWY9dTf1yqBGNjPk2c7wQnhZJdHzv3tgFQiyFK5YR1cjtOV918QHCnptV4r5w==", - "dev": true, - "dependencies": { - "sirv": "^2.0.2" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "dev": true - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", - "dev": true, - "engines": { - "node": "*" - } - }, - "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==", - "dev": true - }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/brace-expansion": { - "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" - } - }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/bundle-require": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bundle-require/-/bundle-require-3.1.2.tgz", - "integrity": "sha512-Of6l6JBAxiyQ5axFxUM6dYeP/W7X2Sozeo/4EYB9sJhL+dqL7TKjg+shwxp6jlu/6ZSERfsYtIpSJ1/x3XkAEA==", - "dev": true, - "dependencies": { - "load-tsconfig": "^0.2.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "peerDependencies": { - "esbuild": ">=0.13" - } - }, - "node_modules/c8": { - "version": "7.12.0", - "resolved": "https://registry.npmjs.org/c8/-/c8-7.12.0.tgz", - "integrity": "sha512-CtgQrHOkyxr5koX1wEUmN/5cfDa2ckbHRA4Gy5LAL0zaCFtVWJS5++n+w4/sr2GWGerBxgTjpKeDclk/Qk6W/A==", - "dev": true, - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@istanbuljs/schema": "^0.1.3", - "find-up": "^5.0.0", - "foreground-child": "^2.0.0", - "istanbul-lib-coverage": "^3.2.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-reports": "^3.1.4", - "rimraf": "^3.0.2", - "test-exclude": "^6.0.0", - "v8-to-istanbul": "^9.0.0", - "yargs": "^16.2.0", - "yargs-parser": "^20.2.9" - }, - "bin": { - "c8": "bin/c8.js" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/cac": { - "version": "6.7.14", - "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", - "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/chai": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz", - "integrity": "sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==", - "dev": true, - "dependencies": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.2", - "deep-eql": "^4.1.2", - "get-func-name": "^2.0.0", - "loupe": "^2.3.1", - "pathval": "^1.1.1", - "type-detect": "^4.0.5" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/check-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/cluster-key-slot": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", - "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/deep-eql": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", - "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", - "dev": true, - "dependencies": { - "type-detect": "^4.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/esbuild": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.18.tgz", - "integrity": "sha512-x/R72SmW3sSFRm5zrrIjAhCeQSAWoni3CmHEqfQrZIQTM3lVCdehdwuIqaOtfC2slvpdlLa62GYoN8SxT23m6Q==", - "dev": true, - "hasInstallScript": true, - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/android-arm": "0.15.18", - "@esbuild/linux-loong64": "0.15.18", - "esbuild-android-64": "0.15.18", - "esbuild-android-arm64": "0.15.18", - "esbuild-darwin-64": "0.15.18", - "esbuild-darwin-arm64": "0.15.18", - "esbuild-freebsd-64": "0.15.18", - "esbuild-freebsd-arm64": "0.15.18", - "esbuild-linux-32": "0.15.18", - "esbuild-linux-64": "0.15.18", - "esbuild-linux-arm": "0.15.18", - "esbuild-linux-arm64": "0.15.18", - "esbuild-linux-mips64le": "0.15.18", - "esbuild-linux-ppc64le": "0.15.18", - "esbuild-linux-riscv64": "0.15.18", - "esbuild-linux-s390x": "0.15.18", - "esbuild-netbsd-64": "0.15.18", - "esbuild-openbsd-64": "0.15.18", - "esbuild-sunos-64": "0.15.18", - "esbuild-windows-32": "0.15.18", - "esbuild-windows-64": "0.15.18", - "esbuild-windows-arm64": "0.15.18" - } - }, - "node_modules/esbuild-android-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.18.tgz", - "integrity": "sha512-wnpt3OXRhcjfIDSZu9bnzT4/TNTDsOUvip0foZOUBG7QbSt//w3QV4FInVJxNhKc/ErhUxc5z4QjHtMi7/TbgA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-android-arm64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.18.tgz", - "integrity": "sha512-G4xu89B8FCzav9XU8EjsXacCKSG2FT7wW9J6hOc18soEHJdtWu03L3TQDGf0geNxfLTtxENKBzMSq9LlbjS8OQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-darwin-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.18.tgz", - "integrity": "sha512-2WAvs95uPnVJPuYKP0Eqx+Dl/jaYseZEUUT1sjg97TJa4oBtbAKnPnl3b5M9l51/nbx7+QAEtuummJZW0sBEmg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-darwin-arm64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.18.tgz", - "integrity": "sha512-tKPSxcTJ5OmNb1btVikATJ8NftlyNlc8BVNtyT/UAr62JFOhwHlnoPrhYWz09akBLHI9nElFVfWSTSRsrZiDUA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-freebsd-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.18.tgz", - "integrity": "sha512-TT3uBUxkteAjR1QbsmvSsjpKjOX6UkCstr8nMr+q7zi3NuZ1oIpa8U41Y8I8dJH2fJgdC3Dj3CXO5biLQpfdZA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-freebsd-arm64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.18.tgz", - "integrity": "sha512-R/oVr+X3Tkh+S0+tL41wRMbdWtpWB8hEAMsOXDumSSa6qJR89U0S/PpLXrGF7Wk/JykfpWNokERUpCeHDl47wA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-linux-32": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.18.tgz", - "integrity": "sha512-lphF3HiCSYtaa9p1DtXndiQEeQDKPl9eN/XNoBf2amEghugNuqXNZA/ZovthNE2aa4EN43WroO0B85xVSjYkbg==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-linux-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.18.tgz", - "integrity": "sha512-hNSeP97IviD7oxLKFuii5sDPJ+QHeiFTFLoLm7NZQligur8poNOWGIgpQ7Qf8Balb69hptMZzyOBIPtY09GZYw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-linux-arm": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.18.tgz", - "integrity": "sha512-UH779gstRblS4aoS2qpMl3wjg7U0j+ygu3GjIeTonCcN79ZvpPee12Qun3vcdxX+37O5LFxz39XeW2I9bybMVA==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-linux-arm64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.18.tgz", - "integrity": "sha512-54qr8kg/6ilcxd+0V3h9rjT4qmjc0CccMVWrjOEM/pEcUzt8X62HfBSeZfT2ECpM7104mk4yfQXkosY8Quptug==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-linux-mips64le": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.18.tgz", - "integrity": "sha512-Mk6Ppwzzz3YbMl/ZZL2P0q1tnYqh/trYZ1VfNP47C31yT0K8t9s7Z077QrDA/guU60tGNp2GOwCQnp+DYv7bxQ==", - "cpu": [ - "mips64el" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-linux-ppc64le": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.18.tgz", - "integrity": "sha512-b0XkN4pL9WUulPTa/VKHx2wLCgvIAbgwABGnKMY19WhKZPT+8BxhZdqz6EgkqCLld7X5qiCY2F/bfpUUlnFZ9w==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-linux-riscv64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.18.tgz", - "integrity": "sha512-ba2COaoF5wL6VLZWn04k+ACZjZ6NYniMSQStodFKH/Pu6RxzQqzsmjR1t9QC89VYJxBeyVPTaHuBMCejl3O/xg==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-linux-s390x": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.18.tgz", - "integrity": "sha512-VbpGuXEl5FCs1wDVp93O8UIzl3ZrglgnSQ+Hu79g7hZu6te6/YHgVJxCM2SqfIila0J3k0csfnf8VD2W7u2kzQ==", - "cpu": [ - "s390x" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-netbsd-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.18.tgz", - "integrity": "sha512-98ukeCdvdX7wr1vUYQzKo4kQ0N2p27H7I11maINv73fVEXt2kyh4K4m9f35U1K43Xc2QGXlzAw0K9yoU7JUjOg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-openbsd-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.18.tgz", - "integrity": "sha512-yK5NCcH31Uae076AyQAXeJzt/vxIo9+omZRKj1pauhk3ITuADzuOx5N2fdHrAKPxN+zH3w96uFKlY7yIn490xQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-sunos-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.18.tgz", - "integrity": "sha512-On22LLFlBeLNj/YF3FT+cXcyKPEI263nflYlAhz5crxtp3yRG1Ugfr7ITyxmCmjm4vbN/dGrb/B7w7U8yJR9yw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-windows-32": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.18.tgz", - "integrity": "sha512-o+eyLu2MjVny/nt+E0uPnBxYuJHBvho8vWsC2lV61A7wwTWC3jkN2w36jtA+yv1UgYkHRihPuQsL23hsCYGcOQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-windows-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.18.tgz", - "integrity": "sha512-qinug1iTTaIIrCorAUjR0fcBk24fjzEedFYhhispP8Oc7SFvs+XeW3YpAKiKp8dRpizl4YYAhxMjlftAMJiaUw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-windows-arm64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.18.tgz", - "integrity": "sha512-q9bsYzegpZcLziq0zgUi5KqGVtfhjxGbnksaBFYmWLxeV/S1fK4OLdq2DFYnXcLMjlZw2L0jLsk1eGoB522WXQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", - "dev": true, - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/foreground-child": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", - "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "node_modules/generic-pool": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", - "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==", - "engines": { - "node": ">= 4" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/handlebars": { - "version": "4.7.7", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", - "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", - "dev": true, - "dependencies": { - "minimist": "^1.2.5", - "neo-async": "^2.6.0", - "source-map": "^0.6.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "handlebars": "bin/handlebars" - }, - "engines": { - "node": ">=0.4.7" - }, - "optionalDependencies": { - "uglify-js": "^3.1.4" - } - }, - "node_modules/handlebars/node_modules/source-map": { - "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" - } - }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-flag": { - "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" - } - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", - "dev": true, - "dependencies": { - "has": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", - "dev": true, - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-reports": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", - "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", - "dev": true, - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/joycon": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", - "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/jsonc-parser": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", - "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", - "dev": true - }, - "node_modules/jsonpath-plus": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-7.2.0.tgz", - "integrity": "sha512-zBfiUPM5nD0YZSBT/o/fbCUlCcepMIdP0CJZxM1+KgA4f2T206f6VAg9e7mX35+KlMaIc5qXW34f3BnwJ3w+RA==", - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/just-clone": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/just-clone/-/just-clone-6.2.0.tgz", - "integrity": "sha512-1IynUYEc/HAwxhi3WDpIpxJbZpMCvvrrmZVqvj9EhpvbH8lls7HhdhiByjL7DkAaWlLIzpC0Xc/VPvy/UxLNjA==" - }, - "node_modules/lilconfig": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz", - "integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, - "node_modules/load-tsconfig": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.3.tgz", - "integrity": "sha512-iyT2MXws+dc2Wi6o3grCFtGXpeMvHmJqS27sMPGtV2eUu4PeFnG+33I8BlFK1t1NWMjOpcx9bridn5yxLDX2gQ==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/local-pkg": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.4.3.tgz", - "integrity": "sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==", - "dev": true, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash.sortby": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", - "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==", - "dev": true - }, - "node_modules/loupe": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz", - "integrity": "sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==", - "dev": true, - "dependencies": { - "get-func-name": "^2.0.0" - } - }, - "node_modules/lunr": { - "version": "2.3.9", - "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", - "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", - "dev": true - }, - "node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/marked": { - "version": "4.2.12", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.2.12.tgz", - "integrity": "sha512-yr8hSKa3Fv4D3jdZmtMMPghgVt6TWbk86WQaWhDloQjRSQhMMYCAro7jP7VDJrjjdV8pxVxMssXS8B8Y5DZ5aw==", - "dev": true, - "bin": { - "marked": "bin/marked.js" - }, - "engines": { - "node": ">= 12" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", - "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/mrmime": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-1.0.1.tgz", - "integrity": "sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "dev": true, - "dependencies": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" - } - }, - "node_modules/nanoid": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", - "dev": true, - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=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==", - "dev": true - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/pathval": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pirates": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", - "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/postcss": { - "version": "8.4.21", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz", - "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - } - ], - "dependencies": { - "nanoid": "^3.3.4", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss-load-config": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz", - "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==", - "dev": true, - "dependencies": { - "lilconfig": "^2.0.5", - "yaml": "^1.10.2" - }, - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": ">=8.0.9", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "postcss": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/redis": { - "version": "4.6.4", - "resolved": "https://registry.npmjs.org/redis/-/redis-4.6.4.tgz", - "integrity": "sha512-wi2tgDdQ+Q8q+PR5FLRx4QvDiWaA+PoJbrzsyFqlClN5R4LplHqN3scs/aGjE//mbz++W19SgxiEnQ27jnCRaA==", - "dependencies": { - "@redis/bloom": "1.2.0", - "@redis/client": "1.5.5", - "@redis/graph": "1.1.0", - "@redis/json": "1.0.4", - "@redis/search": "1.1.1", - "@redis/time-series": "1.0.4" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", - "dev": true, - "dependencies": { - "is-core-module": "^2.9.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rollup": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.12.0.tgz", - "integrity": "sha512-4MZ8kA2HNYahIjz63rzrMMRvDqQDeS9LoriJvMuV0V6zIGysP36e9t4yObUfwdT9h/szXoHQideICftcdZklWg==", - "dev": true, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=14.18.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/shiki": { - "version": "0.12.1", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.12.1.tgz", - "integrity": "sha512-aieaV1m349rZINEBkjxh2QbBvFFQOlgqYTNtCal82hHj4dDZ76oMlQIX+C7ryerBTDiga3e5NfH6smjdJ02BbQ==", - "dev": true, - "dependencies": { - "jsonc-parser": "^3.2.0", - "vscode-oniguruma": "^1.7.0", - "vscode-textmate": "^8.0.0" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "node_modules/sirv": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.2.tgz", - "integrity": "sha512-4Qog6aE29nIjAOKe/wowFTxOdmbEZKb+3tsLljaBRzJwtqto0BChD2zzH0LhgCSXiI+V7X+Y45v14wBZQ1TK3w==", - "dev": true, - "dependencies": { - "@polka/url": "^1.0.0-next.20", - "mrmime": "^1.0.0", - "totalist": "^3.0.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/source-map": { - "version": "0.8.0-beta.0", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", - "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==", - "dev": true, - "dependencies": { - "whatwg-url": "^7.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/sucrase": { - "version": "3.29.0", - "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.29.0.tgz", - "integrity": "sha512-bZPAuGA5SdFHuzqIhTAqt9fvNEo9rESqXIG3oiKdF8K4UmkQxC4KlNL3lVyAErXp+mPvUqZ5l13qx6TrDIGf3A==", - "dev": true, - "dependencies": { - "commander": "^4.0.0", - "glob": "7.1.6", - "lines-and-columns": "^1.1.6", - "mz": "^2.7.0", - "pirates": "^4.0.1", - "ts-interface-checker": "^0.1.9" - }, - "bin": { - "sucrase": "bin/sucrase", - "sucrase-node": "bin/sucrase-node" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/sucrase/node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "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" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/thenify": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", - "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "dev": true, - "dependencies": { - "any-promise": "^1.0.0" - } - }, - "node_modules/thenify-all": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", - "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", - "dev": true, - "dependencies": { - "thenify": ">= 3.1.0 < 4" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/tinypool": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.2.4.tgz", - "integrity": "sha512-Vs3rhkUH6Qq1t5bqtb816oT+HeJTXfwt2cbPH17sWHIYKTotQIFPk3tf2fgqRrVyMDVOc1EnPgzIxfIulXVzwQ==", - "dev": true, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/tinyspy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-1.0.2.tgz", - "integrity": "sha512-bSGlgwLBYf7PnUsQ6WOc6SJ3pGOcd+d8AA6EUnLDDM0kWEstC1JIlSZA3UNliDXhd9ABoS7hiRBDCu+XP/sf1Q==", - "dev": true, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/totalist": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.0.tgz", - "integrity": "sha512-eM+pCBxXO/njtF7vdFsHuqb+ElbxqtI4r5EAvk6grfAFyJ6IvWlSkfZ5T9ozC6xWw3Fj1fGoSmrl0gUs46JVIw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/tr46": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", - "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", - "dev": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/tree-kill": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", - "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", - "dev": true, - "bin": { - "tree-kill": "cli.js" - } - }, - "node_modules/ts-interface-checker": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", - "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", - "dev": true - }, - "node_modules/tsup": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/tsup/-/tsup-6.5.0.tgz", - "integrity": "sha512-36u82r7rYqRHFkD15R20Cd4ercPkbYmuvRkz3Q1LCm5BsiFNUgpo36zbjVhCOgvjyxNBWNKHsaD5Rl8SykfzNA==", - "dev": true, - "dependencies": { - "bundle-require": "^3.1.2", - "cac": "^6.7.12", - "chokidar": "^3.5.1", - "debug": "^4.3.1", - "esbuild": "^0.15.1", - "execa": "^5.0.0", - "globby": "^11.0.3", - "joycon": "^3.0.1", - "postcss-load-config": "^3.0.1", - "resolve-from": "^5.0.0", - "rollup": "^3.2.5", - "source-map": "0.8.0-beta.0", - "sucrase": "^3.20.3", - "tree-kill": "^1.2.2" - }, - "bin": { - "tsup": "dist/cli-default.js", - "tsup-node": "dist/cli-node.js" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@swc/core": "^1", - "postcss": "^8.4.12", - "typescript": "^4.1.0" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "postcss": { - "optional": true - }, - "typescript": { - "optional": true - } - } - }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/typedoc": { - "version": "0.23.24", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.23.24.tgz", - "integrity": "sha512-bfmy8lNQh+WrPYcJbtjQ6JEEsVl/ce1ZIXyXhyW+a1vFrjO39t6J8sL/d6FfAGrJTc7McCXgk9AanYBSNvLdIA==", - "dev": true, - "dependencies": { - "lunr": "^2.3.9", - "marked": "^4.2.5", - "minimatch": "^5.1.2", - "shiki": "^0.12.1" - }, - "bin": { - "typedoc": "bin/typedoc" - }, - "engines": { - "node": ">= 14.14" - }, - "peerDependencies": { - "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x" - } - }, - "node_modules/typedoc-plugin-markdown": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.14.0.tgz", - "integrity": "sha512-UyQLkLRkfTFhLdhSf3RRpA3nNInGn+k6sll2vRXjflaMNwQAAiB61SYbisNZTg16t4K1dt1bPQMMGLrxS0GZ0Q==", - "dev": true, - "dependencies": { - "handlebars": "^4.7.7" - }, - "peerDependencies": { - "typedoc": ">=0.23.0" - } - }, - "node_modules/typedoc/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/typedoc/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/uglify-js": { - "version": "3.17.4", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", - "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", - "dev": true, - "optional": true, - "bin": { - "uglifyjs": "bin/uglifyjs" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/ulid": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/ulid/-/ulid-2.3.0.tgz", - "integrity": "sha512-keqHubrlpvT6G2wH0OEfSW4mquYRcbe/J8NMmveoQOjUqmo+hXtO+ORCpWhdbZ7k72UtY61BL7haGxW6enBnjw==", - "bin": { - "ulid": "bin/cli.js" - } - }, - "node_modules/v8-to-istanbul": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz", - "integrity": "sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/vite": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/vite/-/vite-3.2.5.tgz", - "integrity": "sha512-4mVEpXpSOgrssFZAOmGIr85wPHKvaDAcXqxVxVRZhljkJOMZi1ibLibzjLHzJvcok8BMguLc7g1W6W/GqZbLdQ==", - "dev": true, - "dependencies": { - "esbuild": "^0.15.9", - "postcss": "^8.4.18", - "resolve": "^1.22.1", - "rollup": "^2.79.1" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - }, - "peerDependencies": { - "@types/node": ">= 14", - "less": "*", - "sass": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.4.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "less": { - "optional": true - }, - "sass": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - } - } - }, - "node_modules/vite/node_modules/rollup": { - "version": "2.79.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", - "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==", - "dev": true, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=10.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/vitest": { - "version": "0.20.3", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.20.3.tgz", - "integrity": "sha512-cXMjTbZxBBUUuIF3PUzEGPLJWtIMeURBDXVxckSHpk7xss4JxkiiWh5cnIlfGyfJne2Ii3QpbiRuFL5dMJtljw==", - "dev": true, - "dependencies": { - "@types/chai": "^4.3.1", - "@types/chai-subset": "^1.3.3", - "@types/node": "*", - "chai": "^4.3.6", - "debug": "^4.3.4", - "local-pkg": "^0.4.2", - "tinypool": "^0.2.4", - "tinyspy": "^1.0.0", - "vite": "^2.9.12 || ^3.0.0-0" - }, - "bin": { - "vitest": "vitest.mjs" - }, - "engines": { - "node": ">=v14.16.0" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - }, - "peerDependencies": { - "@edge-runtime/vm": "*", - "@vitest/browser": "*", - "@vitest/ui": "*", - "c8": "*", - "happy-dom": "*", - "jsdom": "*" - }, - "peerDependenciesMeta": { - "@edge-runtime/vm": { - "optional": true - }, - "@vitest/browser": { - "optional": true - }, - "@vitest/ui": { - "optional": true - }, - "c8": { - "optional": true - }, - "happy-dom": { - "optional": true - }, - "jsdom": { - "optional": true - } - } - }, - "node_modules/vscode-oniguruma": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", - "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==", - "dev": true - }, - "node_modules/vscode-textmate": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz", - "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==", - "dev": true - }, - "node_modules/webidl-conversions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", - "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", - "dev": true - }, - "node_modules/whatwg-url": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", - "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", - "dev": true, - "dependencies": { - "lodash.sortby": "^4.7.0", - "tr46": "^1.0.1", - "webidl-conversions": "^4.0.2" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", - "dev": true - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - }, - "dependencies": { - "@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true - }, - "@esbuild/android-arm": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.18.tgz", - "integrity": "sha512-5GT+kcs2WVGjVs7+boataCkO5Fg0y4kCjzkB5bAip7H4jfnOS3dA6KPiww9W1OEKTKeAcUVhdZGvgI65OXmUnw==", - "dev": true, - "optional": true - }, - "@esbuild/linux-loong64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.18.tgz", - "integrity": "sha512-L4jVKS82XVhw2nvzLg/19ClLWg0y27ulRwuP7lcyL6AbUWB5aPglXY3M21mauDQMDfRLs8cQmeT03r/+X3cZYQ==", - "dev": true, - "optional": true - }, - "@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true - }, - "@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", - "dev": true - }, - "@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "dev": true - }, - "@jridgewell/trace-mapping": { - "version": "0.3.17", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", - "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" - } - }, - "@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true - }, - "@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - } - }, - "@polka/url": { - "version": "1.0.0-next.21", - "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.21.tgz", - "integrity": "sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==", - "dev": true - }, - "@redis/bloom": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz", - "integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==", - "requires": {} - }, - "@redis/client": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.5.5.tgz", - "integrity": "sha512-fuMnpDYSjT5JXR9rrCW1YWA4L8N/9/uS4ImT3ZEC/hcaQRI1D/9FvwjriRj1UvepIgzZXthFVKMNRzP/LNL7BQ==", - "requires": { - "cluster-key-slot": "1.1.2", - "generic-pool": "3.9.0", - "yallist": "4.0.0" - } - }, - "@redis/graph": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.0.tgz", - "integrity": "sha512-16yZWngxyXPd+MJxeSr0dqh2AIOi8j9yXKcKCwVaKDbH3HTuETpDVPcLujhFYVPtYrngSco31BUcSa9TH31Gqg==", - "requires": {} - }, - "@redis/json": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.4.tgz", - "integrity": "sha512-LUZE2Gdrhg0Rx7AN+cZkb1e6HjoSKaeeW8rYnt89Tly13GBI5eP4CwDVr+MY8BAYfCg4/N15OUrtLoona9uSgw==", - "requires": {} - }, - "@redis/search": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.1.1.tgz", - "integrity": "sha512-pqCXTc5e7wJJgUuJiC3hBgfoFRoPxYzwn0BEfKgejTM7M/9zP3IpUcqcjgfp8hF+LoV8rHZzcNTz7V+pEIY7LQ==", - "requires": {} - }, - "@redis/time-series": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.0.4.tgz", - "integrity": "sha512-ThUIgo2U/g7cCuZavucQTQzA9g9JbDDY2f64u3AbAoz/8vE2lt2U37LamDUVChhaDA3IRT9R6VvJwqnUfTJzng==", - "requires": {} - }, - "@types/chai": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.4.tgz", - "integrity": "sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw==", - "dev": true - }, - "@types/chai-subset": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@types/chai-subset/-/chai-subset-1.3.3.tgz", - "integrity": "sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==", - "dev": true, - "requires": { - "@types/chai": "*" - } - }, - "@types/istanbul-lib-coverage": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", - "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", - "dev": true - }, - "@types/node": { - "version": "18.11.18", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz", - "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==", - "dev": true - }, - "@vitest/ui": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-0.17.1.tgz", - "integrity": "sha512-B4PGDk5IZ10HT9GzR0NQ96VEphWY9dTf1yqBGNjPk2c7wQnhZJdHzv3tgFQiyFK5YR1cjtOV918QHCnptV4r5w==", - "dev": true, - "requires": { - "sirv": "^2.0.2" - } - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "dev": true - }, - "anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, - "assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", - "dev": true - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "bundle-require": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bundle-require/-/bundle-require-3.1.2.tgz", - "integrity": "sha512-Of6l6JBAxiyQ5axFxUM6dYeP/W7X2Sozeo/4EYB9sJhL+dqL7TKjg+shwxp6jlu/6ZSERfsYtIpSJ1/x3XkAEA==", - "dev": true, - "requires": { - "load-tsconfig": "^0.2.0" - } - }, - "c8": { - "version": "7.12.0", - "resolved": "https://registry.npmjs.org/c8/-/c8-7.12.0.tgz", - "integrity": "sha512-CtgQrHOkyxr5koX1wEUmN/5cfDa2ckbHRA4Gy5LAL0zaCFtVWJS5++n+w4/sr2GWGerBxgTjpKeDclk/Qk6W/A==", - "dev": true, - "requires": { - "@bcoe/v8-coverage": "^0.2.3", - "@istanbuljs/schema": "^0.1.3", - "find-up": "^5.0.0", - "foreground-child": "^2.0.0", - "istanbul-lib-coverage": "^3.2.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-reports": "^3.1.4", - "rimraf": "^3.0.2", - "test-exclude": "^6.0.0", - "v8-to-istanbul": "^9.0.0", - "yargs": "^16.2.0", - "yargs-parser": "^20.2.9" - } - }, - "cac": { - "version": "6.7.14", - "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", - "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", - "dev": true - }, - "chai": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz", - "integrity": "sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==", - "dev": true, - "requires": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.2", - "deep-eql": "^4.1.2", - "get-func-name": "^2.0.0", - "loupe": "^2.3.1", - "pathval": "^1.1.1", - "type-detect": "^4.0.5" - } - }, - "check-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==", - "dev": true - }, - "chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - } - }, - "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "cluster-key-slot": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", - "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==" - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "deep-eql": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", - "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", - "dev": true, - "requires": { - "type-detect": "^4.0.0" - } - }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "requires": { - "path-type": "^4.0.0" - } - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "esbuild": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.18.tgz", - "integrity": "sha512-x/R72SmW3sSFRm5zrrIjAhCeQSAWoni3CmHEqfQrZIQTM3lVCdehdwuIqaOtfC2slvpdlLa62GYoN8SxT23m6Q==", - "dev": true, - "requires": { - "@esbuild/android-arm": "0.15.18", - "@esbuild/linux-loong64": "0.15.18", - "esbuild-android-64": "0.15.18", - "esbuild-android-arm64": "0.15.18", - "esbuild-darwin-64": "0.15.18", - "esbuild-darwin-arm64": "0.15.18", - "esbuild-freebsd-64": "0.15.18", - "esbuild-freebsd-arm64": "0.15.18", - "esbuild-linux-32": "0.15.18", - "esbuild-linux-64": "0.15.18", - "esbuild-linux-arm": "0.15.18", - "esbuild-linux-arm64": "0.15.18", - "esbuild-linux-mips64le": "0.15.18", - "esbuild-linux-ppc64le": "0.15.18", - "esbuild-linux-riscv64": "0.15.18", - "esbuild-linux-s390x": "0.15.18", - "esbuild-netbsd-64": "0.15.18", - "esbuild-openbsd-64": "0.15.18", - "esbuild-sunos-64": "0.15.18", - "esbuild-windows-32": "0.15.18", - "esbuild-windows-64": "0.15.18", - "esbuild-windows-arm64": "0.15.18" - } - }, - "esbuild-android-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.18.tgz", - "integrity": "sha512-wnpt3OXRhcjfIDSZu9bnzT4/TNTDsOUvip0foZOUBG7QbSt//w3QV4FInVJxNhKc/ErhUxc5z4QjHtMi7/TbgA==", - "dev": true, - "optional": true - }, - "esbuild-android-arm64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.18.tgz", - "integrity": "sha512-G4xu89B8FCzav9XU8EjsXacCKSG2FT7wW9J6hOc18soEHJdtWu03L3TQDGf0geNxfLTtxENKBzMSq9LlbjS8OQ==", - "dev": true, - "optional": true - }, - "esbuild-darwin-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.18.tgz", - "integrity": "sha512-2WAvs95uPnVJPuYKP0Eqx+Dl/jaYseZEUUT1sjg97TJa4oBtbAKnPnl3b5M9l51/nbx7+QAEtuummJZW0sBEmg==", - "dev": true, - "optional": true - }, - "esbuild-darwin-arm64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.18.tgz", - "integrity": "sha512-tKPSxcTJ5OmNb1btVikATJ8NftlyNlc8BVNtyT/UAr62JFOhwHlnoPrhYWz09akBLHI9nElFVfWSTSRsrZiDUA==", - "dev": true, - "optional": true - }, - "esbuild-freebsd-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.18.tgz", - "integrity": "sha512-TT3uBUxkteAjR1QbsmvSsjpKjOX6UkCstr8nMr+q7zi3NuZ1oIpa8U41Y8I8dJH2fJgdC3Dj3CXO5biLQpfdZA==", - "dev": true, - "optional": true - }, - "esbuild-freebsd-arm64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.18.tgz", - "integrity": "sha512-R/oVr+X3Tkh+S0+tL41wRMbdWtpWB8hEAMsOXDumSSa6qJR89U0S/PpLXrGF7Wk/JykfpWNokERUpCeHDl47wA==", - "dev": true, - "optional": true - }, - "esbuild-linux-32": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.18.tgz", - "integrity": "sha512-lphF3HiCSYtaa9p1DtXndiQEeQDKPl9eN/XNoBf2amEghugNuqXNZA/ZovthNE2aa4EN43WroO0B85xVSjYkbg==", - "dev": true, - "optional": true - }, - "esbuild-linux-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.18.tgz", - "integrity": "sha512-hNSeP97IviD7oxLKFuii5sDPJ+QHeiFTFLoLm7NZQligur8poNOWGIgpQ7Qf8Balb69hptMZzyOBIPtY09GZYw==", - "dev": true, - "optional": true - }, - "esbuild-linux-arm": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.18.tgz", - "integrity": "sha512-UH779gstRblS4aoS2qpMl3wjg7U0j+ygu3GjIeTonCcN79ZvpPee12Qun3vcdxX+37O5LFxz39XeW2I9bybMVA==", - "dev": true, - "optional": true - }, - "esbuild-linux-arm64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.18.tgz", - "integrity": "sha512-54qr8kg/6ilcxd+0V3h9rjT4qmjc0CccMVWrjOEM/pEcUzt8X62HfBSeZfT2ECpM7104mk4yfQXkosY8Quptug==", - "dev": true, - "optional": true - }, - "esbuild-linux-mips64le": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.18.tgz", - "integrity": "sha512-Mk6Ppwzzz3YbMl/ZZL2P0q1tnYqh/trYZ1VfNP47C31yT0K8t9s7Z077QrDA/guU60tGNp2GOwCQnp+DYv7bxQ==", - "dev": true, - "optional": true - }, - "esbuild-linux-ppc64le": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.18.tgz", - "integrity": "sha512-b0XkN4pL9WUulPTa/VKHx2wLCgvIAbgwABGnKMY19WhKZPT+8BxhZdqz6EgkqCLld7X5qiCY2F/bfpUUlnFZ9w==", - "dev": true, - "optional": true - }, - "esbuild-linux-riscv64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.18.tgz", - "integrity": "sha512-ba2COaoF5wL6VLZWn04k+ACZjZ6NYniMSQStodFKH/Pu6RxzQqzsmjR1t9QC89VYJxBeyVPTaHuBMCejl3O/xg==", - "dev": true, - "optional": true - }, - "esbuild-linux-s390x": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.18.tgz", - "integrity": "sha512-VbpGuXEl5FCs1wDVp93O8UIzl3ZrglgnSQ+Hu79g7hZu6te6/YHgVJxCM2SqfIila0J3k0csfnf8VD2W7u2kzQ==", - "dev": true, - "optional": true - }, - "esbuild-netbsd-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.18.tgz", - "integrity": "sha512-98ukeCdvdX7wr1vUYQzKo4kQ0N2p27H7I11maINv73fVEXt2kyh4K4m9f35U1K43Xc2QGXlzAw0K9yoU7JUjOg==", - "dev": true, - "optional": true - }, - "esbuild-openbsd-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.18.tgz", - "integrity": "sha512-yK5NCcH31Uae076AyQAXeJzt/vxIo9+omZRKj1pauhk3ITuADzuOx5N2fdHrAKPxN+zH3w96uFKlY7yIn490xQ==", - "dev": true, - "optional": true - }, - "esbuild-sunos-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.18.tgz", - "integrity": "sha512-On22LLFlBeLNj/YF3FT+cXcyKPEI263nflYlAhz5crxtp3yRG1Ugfr7ITyxmCmjm4vbN/dGrb/B7w7U8yJR9yw==", - "dev": true, - "optional": true - }, - "esbuild-windows-32": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.18.tgz", - "integrity": "sha512-o+eyLu2MjVny/nt+E0uPnBxYuJHBvho8vWsC2lV61A7wwTWC3jkN2w36jtA+yv1UgYkHRihPuQsL23hsCYGcOQ==", - "dev": true, - "optional": true - }, - "esbuild-windows-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.18.tgz", - "integrity": "sha512-qinug1iTTaIIrCorAUjR0fcBk24fjzEedFYhhispP8Oc7SFvs+XeW3YpAKiKp8dRpizl4YYAhxMjlftAMJiaUw==", - "dev": true, - "optional": true - }, - "esbuild-windows-arm64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.18.tgz", - "integrity": "sha512-q9bsYzegpZcLziq0zgUi5KqGVtfhjxGbnksaBFYmWLxeV/S1fK4OLdq2DFYnXcLMjlZw2L0jLsk1eGoB522WXQ==", - "dev": true, - "optional": true - }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true - }, - "execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - } - }, - "fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - } - }, - "fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", - "dev": true, - "requires": { - "reusify": "^1.0.4" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, - "foreground-child": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", - "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.0", - "signal-exit": "^3.0.2" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "generic-pool": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", - "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==" - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", - "dev": true - }, - "get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true - }, - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - } - }, - "handlebars": { - "version": "4.7.7", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", - "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", - "dev": true, - "requires": { - "minimist": "^1.2.5", - "neo-async": "^2.6.0", - "source-map": "^0.6.1", - "uglify-js": "^3.1.4", - "wordwrap": "^1.0.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true - }, - "ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", - "dev": true - }, - "istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", - "dev": true, - "requires": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" - } - }, - "istanbul-reports": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", - "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", - "dev": true, - "requires": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - } - }, - "joycon": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", - "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", - "dev": true - }, - "jsonc-parser": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", - "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", - "dev": true - }, - "jsonpath-plus": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-7.2.0.tgz", - "integrity": "sha512-zBfiUPM5nD0YZSBT/o/fbCUlCcepMIdP0CJZxM1+KgA4f2T206f6VAg9e7mX35+KlMaIc5qXW34f3BnwJ3w+RA==" - }, - "just-clone": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/just-clone/-/just-clone-6.2.0.tgz", - "integrity": "sha512-1IynUYEc/HAwxhi3WDpIpxJbZpMCvvrrmZVqvj9EhpvbH8lls7HhdhiByjL7DkAaWlLIzpC0Xc/VPvy/UxLNjA==" - }, - "lilconfig": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz", - "integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==", - "dev": true - }, - "lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, - "load-tsconfig": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.3.tgz", - "integrity": "sha512-iyT2MXws+dc2Wi6o3grCFtGXpeMvHmJqS27sMPGtV2eUu4PeFnG+33I8BlFK1t1NWMjOpcx9bridn5yxLDX2gQ==", - "dev": true - }, - "local-pkg": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.4.3.tgz", - "integrity": "sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==", - "dev": true - }, - "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "requires": { - "p-locate": "^5.0.0" - } - }, - "lodash.sortby": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", - "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==", - "dev": true - }, - "loupe": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz", - "integrity": "sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==", - "dev": true, - "requires": { - "get-func-name": "^2.0.0" - } - }, - "lunr": { - "version": "2.3.9", - "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", - "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", - "dev": true - }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "requires": { - "semver": "^6.0.0" - } - }, - "marked": { - "version": "4.2.12", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.2.12.tgz", - "integrity": "sha512-yr8hSKa3Fv4D3jdZmtMMPghgVt6TWbk86WQaWhDloQjRSQhMMYCAro7jP7VDJrjjdV8pxVxMssXS8B8Y5DZ5aw==", - "dev": true - }, - "merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true - }, - "micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, - "requires": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", - "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", - "dev": true - }, - "mrmime": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-1.0.1.tgz", - "integrity": "sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==", - "dev": true - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "dev": true, - "requires": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" - } - }, - "nanoid": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", - "dev": true - }, - "neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "requires": { - "path-key": "^3.0.0" - } - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "requires": { - "p-limit": "^3.0.2" - } - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "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==", - "dev": true - }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true - }, - "pathval": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", - "dev": true - }, - "picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true - }, - "pirates": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", - "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", - "dev": true - }, - "postcss": { - "version": "8.4.21", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz", - "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==", - "dev": true, - "requires": { - "nanoid": "^3.3.4", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - } - }, - "postcss-load-config": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz", - "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==", - "dev": true, - "requires": { - "lilconfig": "^2.0.5", - "yaml": "^1.10.2" - } - }, - "punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", - "dev": true - }, - "queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true - }, - "readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "redis": { - "version": "4.6.4", - "resolved": "https://registry.npmjs.org/redis/-/redis-4.6.4.tgz", - "integrity": "sha512-wi2tgDdQ+Q8q+PR5FLRx4QvDiWaA+PoJbrzsyFqlClN5R4LplHqN3scs/aGjE//mbz++W19SgxiEnQ27jnCRaA==", - "requires": { - "@redis/bloom": "1.2.0", - "@redis/client": "1.5.5", - "@redis/graph": "1.1.0", - "@redis/json": "1.0.4", - "@redis/search": "1.1.1", - "@redis/time-series": "1.0.4" - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true - }, - "resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", - "dev": true, - "requires": { - "is-core-module": "^2.9.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - }, - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - }, - "reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "rollup": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.12.0.tgz", - "integrity": "sha512-4MZ8kA2HNYahIjz63rzrMMRvDqQDeS9LoriJvMuV0V6zIGysP36e9t4yObUfwdT9h/szXoHQideICftcdZklWg==", - "dev": true, - "requires": { - "fsevents": "~2.3.2" - } - }, - "run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "requires": { - "queue-microtask": "^1.2.2" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "shiki": { - "version": "0.12.1", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.12.1.tgz", - "integrity": "sha512-aieaV1m349rZINEBkjxh2QbBvFFQOlgqYTNtCal82hHj4dDZ76oMlQIX+C7ryerBTDiga3e5NfH6smjdJ02BbQ==", - "dev": true, - "requires": { - "jsonc-parser": "^3.2.0", - "vscode-oniguruma": "^1.7.0", - "vscode-textmate": "^8.0.0" - } - }, - "signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "sirv": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.2.tgz", - "integrity": "sha512-4Qog6aE29nIjAOKe/wowFTxOdmbEZKb+3tsLljaBRzJwtqto0BChD2zzH0LhgCSXiI+V7X+Y45v14wBZQ1TK3w==", - "dev": true, - "requires": { - "@polka/url": "^1.0.0-next.20", - "mrmime": "^1.0.0", - "totalist": "^3.0.0" - } - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - }, - "source-map": { - "version": "0.8.0-beta.0", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", - "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==", - "dev": true, - "requires": { - "whatwg-url": "^7.0.0" - } - }, - "source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", - "dev": true - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true - }, - "sucrase": { - "version": "3.29.0", - "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.29.0.tgz", - "integrity": "sha512-bZPAuGA5SdFHuzqIhTAqt9fvNEo9rESqXIG3oiKdF8K4UmkQxC4KlNL3lVyAErXp+mPvUqZ5l13qx6TrDIGf3A==", - "dev": true, - "requires": { - "commander": "^4.0.0", - "glob": "7.1.6", - "lines-and-columns": "^1.1.6", - "mz": "^2.7.0", - "pirates": "^4.0.1", - "ts-interface-checker": "^0.1.9" - }, - "dependencies": { - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "supports-preserve-symlinks-flag": { - "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 - }, - "test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "requires": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - } - }, - "thenify": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", - "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "dev": true, - "requires": { - "any-promise": "^1.0.0" - } - }, - "thenify-all": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", - "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", - "dev": true, - "requires": { - "thenify": ">= 3.1.0 < 4" - } - }, - "tinypool": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.2.4.tgz", - "integrity": "sha512-Vs3rhkUH6Qq1t5bqtb816oT+HeJTXfwt2cbPH17sWHIYKTotQIFPk3tf2fgqRrVyMDVOc1EnPgzIxfIulXVzwQ==", - "dev": true - }, - "tinyspy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-1.0.2.tgz", - "integrity": "sha512-bSGlgwLBYf7PnUsQ6WOc6SJ3pGOcd+d8AA6EUnLDDM0kWEstC1JIlSZA3UNliDXhd9ABoS7hiRBDCu+XP/sf1Q==", - "dev": true - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "totalist": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.0.tgz", - "integrity": "sha512-eM+pCBxXO/njtF7vdFsHuqb+ElbxqtI4r5EAvk6grfAFyJ6IvWlSkfZ5T9ozC6xWw3Fj1fGoSmrl0gUs46JVIw==", - "dev": true - }, - "tr46": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", - "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "tree-kill": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", - "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", - "dev": true - }, - "ts-interface-checker": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", - "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", - "dev": true - }, - "tsup": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/tsup/-/tsup-6.5.0.tgz", - "integrity": "sha512-36u82r7rYqRHFkD15R20Cd4ercPkbYmuvRkz3Q1LCm5BsiFNUgpo36zbjVhCOgvjyxNBWNKHsaD5Rl8SykfzNA==", - "dev": true, - "requires": { - "bundle-require": "^3.1.2", - "cac": "^6.7.12", - "chokidar": "^3.5.1", - "debug": "^4.3.1", - "esbuild": "^0.15.1", - "execa": "^5.0.0", - "globby": "^11.0.3", - "joycon": "^3.0.1", - "postcss-load-config": "^3.0.1", - "resolve-from": "^5.0.0", - "rollup": "^3.2.5", - "source-map": "0.8.0-beta.0", - "sucrase": "^3.20.3", - "tree-kill": "^1.2.2" - } - }, - "type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true - }, - "typedoc": { - "version": "0.23.24", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.23.24.tgz", - "integrity": "sha512-bfmy8lNQh+WrPYcJbtjQ6JEEsVl/ce1ZIXyXhyW+a1vFrjO39t6J8sL/d6FfAGrJTc7McCXgk9AanYBSNvLdIA==", - "dev": true, - "requires": { - "lunr": "^2.3.9", - "marked": "^4.2.5", - "minimatch": "^5.1.2", - "shiki": "^0.12.1" - }, - "dependencies": { - "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0" - } - }, - "minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, - "requires": { - "brace-expansion": "^2.0.1" - } - } - } - }, - "typedoc-plugin-markdown": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.14.0.tgz", - "integrity": "sha512-UyQLkLRkfTFhLdhSf3RRpA3nNInGn+k6sll2vRXjflaMNwQAAiB61SYbisNZTg16t4K1dt1bPQMMGLrxS0GZ0Q==", - "dev": true, - "requires": { - "handlebars": "^4.7.7" - } - }, - "typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", - "dev": true - }, - "uglify-js": { - "version": "3.17.4", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", - "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", - "dev": true, - "optional": true - }, - "ulid": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/ulid/-/ulid-2.3.0.tgz", - "integrity": "sha512-keqHubrlpvT6G2wH0OEfSW4mquYRcbe/J8NMmveoQOjUqmo+hXtO+ORCpWhdbZ7k72UtY61BL7haGxW6enBnjw==" - }, - "v8-to-istanbul": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz", - "integrity": "sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0" - } - }, - "vite": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/vite/-/vite-3.2.5.tgz", - "integrity": "sha512-4mVEpXpSOgrssFZAOmGIr85wPHKvaDAcXqxVxVRZhljkJOMZi1ibLibzjLHzJvcok8BMguLc7g1W6W/GqZbLdQ==", - "dev": true, - "requires": { - "esbuild": "^0.15.9", - "fsevents": "~2.3.2", - "postcss": "^8.4.18", - "resolve": "^1.22.1", - "rollup": "^2.79.1" - }, - "dependencies": { - "rollup": { - "version": "2.79.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", - "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==", - "dev": true, - "requires": { - "fsevents": "~2.3.2" - } - } - } - }, - "vitest": { - "version": "0.20.3", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.20.3.tgz", - "integrity": "sha512-cXMjTbZxBBUUuIF3PUzEGPLJWtIMeURBDXVxckSHpk7xss4JxkiiWh5cnIlfGyfJne2Ii3QpbiRuFL5dMJtljw==", - "dev": true, - "requires": { - "@types/chai": "^4.3.1", - "@types/chai-subset": "^1.3.3", - "@types/node": "*", - "chai": "^4.3.6", - "debug": "^4.3.4", - "local-pkg": "^0.4.2", - "tinypool": "^0.2.4", - "tinyspy": "^1.0.0", - "vite": "^2.9.12 || ^3.0.0-0" - } - }, - "vscode-oniguruma": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", - "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==", - "dev": true - }, - "vscode-textmate": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz", - "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==", - "dev": true - }, - "webidl-conversions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", - "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", - "dev": true - }, - "whatwg-url": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", - "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", - "dev": true, - "requires": { - "lodash.sortby": "^4.7.0", - "tr46": "^1.0.1", - "webidl-conversions": "^4.0.2" - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", - "dev": true - }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, - "y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "dev": true - }, - "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - } - }, - "yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true - }, - "yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true - } - } -} diff --git a/package.json b/package.json index 55d3760..dfb831f 100644 --- a/package.json +++ b/package.json @@ -3,33 +3,30 @@ "version": "0.0.0", "description": "Object mapping, and more, for Redis and Node.js. Written in TypeScript.", "main": "dist/index.js", - "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "require": "./dist/index.js", + "default": "./dist/index.js" + }, + "./types": { + "types": "./dist/typings/index.d.ts", + "require": "./dist/typings/index.js", + "default": "./dist/typings/index.js" + }, + "./package.json": "./package.json", + "./dist/utils/symbols.js": null + }, "files": [ - "README.md", - "CHANGELOG", - "LICENSE", "logo.svg", - "dist/**/*", - "docs/**/*" + "dist/**/*" ], "scripts": { - "build": "tsup", + "build": "rm -rf ./dist && tsc", "docs": "rm -rf ./docs && typedoc", "test": "vitest", "test:coverage": "vitest run --coverage", - "test:ui": "vitest --ui", - "test:unit": "vitest --dir ./spec/unit", - "test:functional": "vitest --dir ./spec/functional" - }, - "tsup": { - "entry": [ - "lib/index.ts" - ], - "clean": true, - "dts": true, - "noExternal": [ - "ulid" - ] + "test:ui": "vitest --ui" }, "repository": "github:redis/redis-om-node", "homepage": "https://github.com/redis/redis-om-node#readme", @@ -39,23 +36,31 @@ ], "license": "MIT", "author": "Guy Royse (http://guyroyse.com/)", + "contributors": [ + "DidaS " + ], "engines": { "node": ">= 14" }, "devDependencies": { - "@types/node": "^18.0.0", - "@vitest/ui": "^0.17.1", - "c8": "^7.11.3", - "tsup": "^6.1.2", - "typedoc": "^0.23.2", - "typedoc-plugin-markdown": "^3.13.1", - "typescript": "^4.7.2", - "vitest": "^0.20.0" + "@microsoft/tsdoc": "^0.14.2", + "@microsoft/tsdoc-config": "^0.16.2", + "@types/node": "^20.5.6", + "@typescript-eslint/eslint-plugin": "^6.4.1", + "@typescript-eslint/parser": "^6.4.1", + "c8": "^8.0.1", + "eslint": "^8.48.0", + "eslint-plugin-import": "^2.28.1", + "eslint-plugin-tsdoc": "^0.2.17", + "redis-om": "0.4.2", + "ts-node": "^10.9.1", + "typescript": "^5.2.2", + "vitest": "^0.34.3" }, "dependencies": { - "jsonpath-plus": "^7.2.0", - "just-clone": "^6.1.1", - "redis": "^4.6.4", - "ulid": "^2.3.0" + "@infinite-fansub/logger": "^2.2.1", + "colours.js": "^3.1.2", + "redis": "^4.6.8", + "tslib": "^2.6.2" } } \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..7fd1673 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,2674 @@ +lockfileVersion: '6.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +dependencies: + '@infinite-fansub/logger': + specifier: ^2.2.1 + version: 2.2.1 + colours.js: + specifier: ^3.1.2 + version: 3.1.2 + redis: + specifier: ^4.6.8 + version: 4.6.8 + tslib: + specifier: ^2.6.2 + version: 2.6.2 + +devDependencies: + '@microsoft/tsdoc': + specifier: ^0.14.2 + version: 0.14.2 + '@microsoft/tsdoc-config': + specifier: ^0.16.2 + version: 0.16.2 + '@types/node': + specifier: ^20.5.6 + version: 20.5.6 + '@typescript-eslint/eslint-plugin': + specifier: ^6.4.1 + version: 6.4.1(@typescript-eslint/parser@6.4.1)(eslint@8.48.0)(typescript@5.2.2) + '@typescript-eslint/parser': + specifier: ^6.4.1 + version: 6.4.1(eslint@8.48.0)(typescript@5.2.2) + c8: + specifier: ^8.0.1 + version: 8.0.1 + eslint: + specifier: ^8.48.0 + version: 8.48.0 + eslint-plugin-import: + specifier: ^2.28.1 + version: 2.28.1(@typescript-eslint/parser@6.4.1)(eslint@8.48.0) + eslint-plugin-tsdoc: + specifier: ^0.2.17 + version: 0.2.17 + redis-om: + specifier: 0.4.2 + version: 0.4.2 + ts-node: + specifier: ^10.9.1 + version: 10.9.1(@types/node@20.5.6)(typescript@5.2.2) + typescript: + specifier: ^5.2.2 + version: 5.2.2 + vitest: + specifier: ^0.34.3 + version: 0.34.3 + +packages: + + /@aashutoshrathi/word-wrap@1.2.6: + resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==} + engines: {node: '>=0.10.0'} + dev: true + + /@bcoe/v8-coverage@0.2.3: + resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} + dev: true + + /@cspotcode/source-map-support@0.8.1: + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/trace-mapping': 0.3.9 + dev: true + + /@esbuild/android-arm64@0.18.20: + resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-arm@0.18.20: + resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-x64@0.18.20: + resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-arm64@0.18.20: + resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-x64@0.18.20: + resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-arm64@0.18.20: + resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-x64@0.18.20: + resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm64@0.18.20: + resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm@0.18.20: + resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ia32@0.18.20: + resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-loong64@0.18.20: + resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-mips64el@0.18.20: + resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ppc64@0.18.20: + resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-riscv64@0.18.20: + resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-s390x@0.18.20: + resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-x64@0.18.20: + resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/netbsd-x64@0.18.20: + resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/openbsd-x64@0.18.20: + resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/sunos-x64@0.18.20: + resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-arm64@0.18.20: + resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-ia32@0.18.20: + resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-x64@0.18.20: + resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@eslint-community/eslint-utils@4.4.0(eslint@8.48.0): + resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + dependencies: + eslint: 8.48.0 + eslint-visitor-keys: 3.4.3 + dev: true + + /@eslint-community/regexpp@4.8.0: + resolution: {integrity: sha512-JylOEEzDiOryeUnFbQz+oViCXS0KsvR1mvHkoMiu5+UiBvy+RYX7tzlIIIEstF/gVa2tj9AQXk3dgnxv6KxhFg==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + dev: true + + /@eslint/eslintrc@2.1.2: + resolution: {integrity: sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + ajv: 6.12.6 + debug: 4.3.4 + espree: 9.6.1 + globals: 13.21.0 + ignore: 5.2.4 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@eslint/js@8.48.0: + resolution: {integrity: sha512-ZSjtmelB7IJfWD2Fvb7+Z+ChTIKWq6kjda95fLcQKNS5aheVHn4IkfgRQE3sIIzTcSLwLcLZUD9UBt+V7+h+Pw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /@humanwhocodes/config-array@0.11.10: + resolution: {integrity: sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==} + engines: {node: '>=10.10.0'} + dependencies: + '@humanwhocodes/object-schema': 1.2.1 + debug: 4.3.4 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@humanwhocodes/module-importer@1.0.1: + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + dev: true + + /@humanwhocodes/object-schema@1.2.1: + resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} + dev: true + + /@infinite-fansub/logger@2.2.1: + resolution: {integrity: sha512-b2alJmSUwmmjH8i6emBvF4lCjQeLg8Gsj6oWjZXbPKkLHz3edSSr7m58KGnN2//osawGFCp4He0mkZGhpTwHCA==} + dependencies: + colours.js: 3.1.2 + tslib: 2.6.2 + dev: false + + /@istanbuljs/schema@0.1.3: + resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} + engines: {node: '>=8'} + dev: true + + /@jest/schemas@29.6.3: + resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@sinclair/typebox': 0.27.8 + dev: true + + /@jridgewell/resolve-uri@3.1.1: + resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} + engines: {node: '>=6.0.0'} + dev: true + + /@jridgewell/sourcemap-codec@1.4.15: + resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + dev: true + + /@jridgewell/trace-mapping@0.3.19: + resolution: {integrity: sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==} + dependencies: + '@jridgewell/resolve-uri': 3.1.1 + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + + /@jridgewell/trace-mapping@0.3.9: + resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + dependencies: + '@jridgewell/resolve-uri': 3.1.1 + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + + /@microsoft/tsdoc-config@0.16.2: + resolution: {integrity: sha512-OGiIzzoBLgWWR0UdRJX98oYO+XKGf7tiK4Zk6tQ/E4IJqGCe7dvkTvgDZV5cFJUzLGDOjeAXrnZoA6QkVySuxw==} + dependencies: + '@microsoft/tsdoc': 0.14.2 + ajv: 6.12.6 + jju: 1.4.0 + resolve: 1.19.0 + dev: true + + /@microsoft/tsdoc@0.14.2: + resolution: {integrity: sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==} + dev: true + + /@nodelib/fs.scandir@2.1.5: + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + dev: true + + /@nodelib/fs.stat@2.0.5: + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + dev: true + + /@nodelib/fs.walk@1.2.8: + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.15.0 + dev: true + + /@redis/bloom@1.2.0(@redis/client@1.5.9): + resolution: {integrity: sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==} + peerDependencies: + '@redis/client': ^1.0.0 + dependencies: + '@redis/client': 1.5.9 + + /@redis/client@1.5.9: + resolution: {integrity: sha512-SffgN+P1zdWJWSXBvJeynvEnmnZrYmtKSRW00xl8pOPFOMJjxRR9u0frSxJpPR6Y4V+k54blJjGW7FgxbTI7bQ==} + engines: {node: '>=14'} + dependencies: + cluster-key-slot: 1.1.2 + generic-pool: 3.9.0 + yallist: 4.0.0 + + /@redis/graph@1.1.0(@redis/client@1.5.9): + resolution: {integrity: sha512-16yZWngxyXPd+MJxeSr0dqh2AIOi8j9yXKcKCwVaKDbH3HTuETpDVPcLujhFYVPtYrngSco31BUcSa9TH31Gqg==} + peerDependencies: + '@redis/client': ^1.0.0 + dependencies: + '@redis/client': 1.5.9 + + /@redis/json@1.0.4(@redis/client@1.5.9): + resolution: {integrity: sha512-LUZE2Gdrhg0Rx7AN+cZkb1e6HjoSKaeeW8rYnt89Tly13GBI5eP4CwDVr+MY8BAYfCg4/N15OUrtLoona9uSgw==} + peerDependencies: + '@redis/client': ^1.0.0 + dependencies: + '@redis/client': 1.5.9 + + /@redis/search@1.1.3(@redis/client@1.5.9): + resolution: {integrity: sha512-4Dg1JjvCevdiCBTZqjhKkGoC5/BcB7k9j99kdMnaXFXg8x4eyOIVg9487CMv7/BUVkFLZCaIh8ead9mU15DNng==} + peerDependencies: + '@redis/client': ^1.0.0 + dependencies: + '@redis/client': 1.5.9 + + /@redis/time-series@1.0.5(@redis/client@1.5.9): + resolution: {integrity: sha512-IFjIgTusQym2B5IZJG3XKr5llka7ey84fw/NOYqESP5WUfQs9zz1ww/9+qoz4ka/S6KcGBodzlCeZ5UImKbscg==} + peerDependencies: + '@redis/client': ^1.0.0 + dependencies: + '@redis/client': 1.5.9 + + /@sinclair/typebox@0.27.8: + resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + dev: true + + /@tsconfig/node10@1.0.9: + resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==} + dev: true + + /@tsconfig/node12@1.0.11: + resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} + dev: true + + /@tsconfig/node14@1.0.3: + resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} + dev: true + + /@tsconfig/node16@1.0.4: + resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + dev: true + + /@types/chai-subset@1.3.3: + resolution: {integrity: sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==} + dependencies: + '@types/chai': 4.3.5 + dev: true + + /@types/chai@4.3.5: + resolution: {integrity: sha512-mEo1sAde+UCE6b2hxn332f1g1E8WfYRu6p5SvTKr2ZKC1f7gFJXk4h5PyGP9Dt6gCaG8y8XhwnXWC6Iy2cmBng==} + dev: true + + /@types/istanbul-lib-coverage@2.0.4: + resolution: {integrity: sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==} + dev: true + + /@types/json-schema@7.0.12: + resolution: {integrity: sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==} + dev: true + + /@types/json5@0.0.29: + resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + dev: true + + /@types/node@20.5.6: + resolution: {integrity: sha512-Gi5wRGPbbyOTX+4Y2iULQ27oUPrefaB0PxGQJnfyWN3kvEDGM3mIB5M/gQLmitZf7A9FmLeaqxD3L1CXpm3VKQ==} + dev: true + + /@types/semver@7.5.0: + resolution: {integrity: sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==} + dev: true + + /@typescript-eslint/eslint-plugin@6.4.1(@typescript-eslint/parser@6.4.1)(eslint@8.48.0)(typescript@5.2.2): + resolution: {integrity: sha512-3F5PtBzUW0dYlq77Lcqo13fv+58KDwUib3BddilE8ajPJT+faGgxmI9Sw+I8ZS22BYwoir9ZhNXcLi+S+I2bkw==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@eslint-community/regexpp': 4.8.0 + '@typescript-eslint/parser': 6.4.1(eslint@8.48.0)(typescript@5.2.2) + '@typescript-eslint/scope-manager': 6.4.1 + '@typescript-eslint/type-utils': 6.4.1(eslint@8.48.0)(typescript@5.2.2) + '@typescript-eslint/utils': 6.4.1(eslint@8.48.0)(typescript@5.2.2) + '@typescript-eslint/visitor-keys': 6.4.1 + debug: 4.3.4 + eslint: 8.48.0 + graphemer: 1.4.0 + ignore: 5.2.4 + natural-compare: 1.4.0 + semver: 7.5.4 + ts-api-utils: 1.0.2(typescript@5.2.2) + typescript: 5.2.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/parser@6.4.1(eslint@8.48.0)(typescript@5.2.2): + resolution: {integrity: sha512-610G6KHymg9V7EqOaNBMtD1GgpAmGROsmfHJPXNLCU9bfIuLrkdOygltK784F6Crboyd5tBFayPB7Sf0McrQwg==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/scope-manager': 6.4.1 + '@typescript-eslint/types': 6.4.1 + '@typescript-eslint/typescript-estree': 6.4.1(typescript@5.2.2) + '@typescript-eslint/visitor-keys': 6.4.1 + debug: 4.3.4 + eslint: 8.48.0 + typescript: 5.2.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/scope-manager@6.4.1: + resolution: {integrity: sha512-p/OavqOQfm4/Hdrr7kvacOSFjwQ2rrDVJRPxt/o0TOWdFnjJptnjnZ+sYDR7fi4OimvIuKp+2LCkc+rt9fIW+A==} + engines: {node: ^16.0.0 || >=18.0.0} + dependencies: + '@typescript-eslint/types': 6.4.1 + '@typescript-eslint/visitor-keys': 6.4.1 + dev: true + + /@typescript-eslint/type-utils@6.4.1(eslint@8.48.0)(typescript@5.2.2): + resolution: {integrity: sha512-7ON8M8NXh73SGZ5XvIqWHjgX2f+vvaOarNliGhjrJnv1vdjG0LVIz+ToYfPirOoBi56jxAKLfsLm40+RvxVVXA==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/typescript-estree': 6.4.1(typescript@5.2.2) + '@typescript-eslint/utils': 6.4.1(eslint@8.48.0)(typescript@5.2.2) + debug: 4.3.4 + eslint: 8.48.0 + ts-api-utils: 1.0.2(typescript@5.2.2) + typescript: 5.2.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/types@6.4.1: + resolution: {integrity: sha512-zAAopbNuYu++ijY1GV2ylCsQsi3B8QvfPHVqhGdDcbx/NK5lkqMnCGU53amAjccSpk+LfeONxwzUhDzArSfZJg==} + engines: {node: ^16.0.0 || >=18.0.0} + dev: true + + /@typescript-eslint/typescript-estree@6.4.1(typescript@5.2.2): + resolution: {integrity: sha512-xF6Y7SatVE/OyV93h1xGgfOkHr2iXuo8ip0gbfzaKeGGuKiAnzS+HtVhSPx8Www243bwlW8IF7X0/B62SzFftg==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/types': 6.4.1 + '@typescript-eslint/visitor-keys': 6.4.1 + debug: 4.3.4 + globby: 11.1.0 + is-glob: 4.0.3 + semver: 7.5.4 + ts-api-utils: 1.0.2(typescript@5.2.2) + typescript: 5.2.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/utils@6.4.1(eslint@8.48.0)(typescript@5.2.2): + resolution: {integrity: sha512-F/6r2RieNeorU0zhqZNv89s9bDZSovv3bZQpUNOmmQK1L80/cV4KEu95YUJWi75u5PhboFoKUJBnZ4FQcoqhDw==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.48.0) + '@types/json-schema': 7.0.12 + '@types/semver': 7.5.0 + '@typescript-eslint/scope-manager': 6.4.1 + '@typescript-eslint/types': 6.4.1 + '@typescript-eslint/typescript-estree': 6.4.1(typescript@5.2.2) + eslint: 8.48.0 + semver: 7.5.4 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /@typescript-eslint/visitor-keys@6.4.1: + resolution: {integrity: sha512-y/TyRJsbZPkJIZQXrHfdnxVnxyKegnpEvnRGNam7s3TRR2ykGefEWOhaef00/UUN3IZxizS7BTO3svd3lCOJRQ==} + engines: {node: ^16.0.0 || >=18.0.0} + dependencies: + '@typescript-eslint/types': 6.4.1 + eslint-visitor-keys: 3.4.3 + dev: true + + /@vitest/expect@0.34.3: + resolution: {integrity: sha512-F8MTXZUYRBVsYL1uoIft1HHWhwDbSzwAU9Zgh8S6WFC3YgVb4AnFV2GXO3P5Em8FjEYaZtTnQYoNwwBrlOMXgg==} + dependencies: + '@vitest/spy': 0.34.3 + '@vitest/utils': 0.34.3 + chai: 4.3.8 + dev: true + + /@vitest/runner@0.34.3: + resolution: {integrity: sha512-lYNq7N3vR57VMKMPLVvmJoiN4bqwzZ1euTW+XXYH5kzr3W/+xQG3b41xJn9ChJ3AhYOSoweu974S1V3qDcFESA==} + dependencies: + '@vitest/utils': 0.34.3 + p-limit: 4.0.0 + pathe: 1.1.1 + dev: true + + /@vitest/snapshot@0.34.3: + resolution: {integrity: sha512-QyPaE15DQwbnIBp/yNJ8lbvXTZxS00kRly0kfFgAD5EYmCbYcA+1EEyRalc93M0gosL/xHeg3lKAClIXYpmUiQ==} + dependencies: + magic-string: 0.30.3 + pathe: 1.1.1 + pretty-format: 29.6.3 + dev: true + + /@vitest/spy@0.34.3: + resolution: {integrity: sha512-N1V0RFQ6AI7CPgzBq9kzjRdPIgThC340DGjdKdPSE8r86aUSmeliTUgkTqLSgtEwWWsGfBQ+UetZWhK0BgJmkQ==} + dependencies: + tinyspy: 2.1.1 + dev: true + + /@vitest/utils@0.34.3: + resolution: {integrity: sha512-kiSnzLG6m/tiT0XEl4U2H8JDBjFtwVlaE8I3QfGiMFR0QvnRDfYfdP3YvTBWM/6iJDAyaPY6yVQiCTUc7ZzTHA==} + dependencies: + diff-sequences: 29.6.3 + loupe: 2.3.6 + pretty-format: 29.6.3 + dev: true + + /acorn-jsx@5.3.2(acorn@8.10.0): + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + acorn: 8.10.0 + dev: true + + /acorn-walk@8.2.0: + resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} + engines: {node: '>=0.4.0'} + dev: true + + /acorn@8.10.0: + resolution: {integrity: sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true + + /ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + dev: true + + /ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + dev: true + + /ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + dependencies: + color-convert: 2.0.1 + dev: true + + /ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + dev: true + + /arg@4.1.3: + resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} + dev: true + + /argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + dev: true + + /array-buffer-byte-length@1.0.0: + resolution: {integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==} + dependencies: + call-bind: 1.0.2 + is-array-buffer: 3.0.2 + dev: true + + /array-includes@3.1.6: + resolution: {integrity: sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.22.1 + get-intrinsic: 1.2.1 + is-string: 1.0.7 + dev: true + + /array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + dev: true + + /array.prototype.findlastindex@1.2.2: + resolution: {integrity: sha512-tb5thFFlUcp7NdNF6/MpDk/1r/4awWG1FIz3YqDf+/zJSTezBb+/5WViH41obXULHVpDzoiCLpJ/ZO9YbJMsdw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.22.1 + es-shim-unscopables: 1.0.0 + get-intrinsic: 1.2.1 + dev: true + + /array.prototype.flat@1.3.1: + resolution: {integrity: sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.22.1 + es-shim-unscopables: 1.0.0 + dev: true + + /array.prototype.flatmap@1.3.1: + resolution: {integrity: sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.22.1 + es-shim-unscopables: 1.0.0 + dev: true + + /arraybuffer.prototype.slice@1.0.1: + resolution: {integrity: sha512-09x0ZWFEjj4WD8PDbykUwo3t9arLn8NIzmmYEJFpYekOAQjpkGSyrQhNoRTcwwcFRu+ycWF78QZ63oWTqSjBcw==} + engines: {node: '>= 0.4'} + dependencies: + array-buffer-byte-length: 1.0.0 + call-bind: 1.0.2 + define-properties: 1.2.0 + get-intrinsic: 1.2.1 + is-array-buffer: 3.0.2 + is-shared-array-buffer: 1.0.2 + dev: true + + /assertion-error@1.1.0: + resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} + dev: true + + /available-typed-arrays@1.0.5: + resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} + engines: {node: '>= 0.4'} + dev: true + + /balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + dev: true + + /brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + dev: true + + /braces@3.0.2: + resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} + engines: {node: '>=8'} + dependencies: + fill-range: 7.0.1 + dev: true + + /c8@8.0.1: + resolution: {integrity: sha512-EINpopxZNH1mETuI0DzRA4MZpAUH+IFiRhnmFD3vFr3vdrgxqi3VfE3KL0AIL+zDq8rC9bZqwM/VDmmoe04y7w==} + engines: {node: '>=12'} + hasBin: true + dependencies: + '@bcoe/v8-coverage': 0.2.3 + '@istanbuljs/schema': 0.1.3 + find-up: 5.0.0 + foreground-child: 2.0.0 + istanbul-lib-coverage: 3.2.0 + istanbul-lib-report: 3.0.1 + istanbul-reports: 3.1.6 + rimraf: 3.0.2 + test-exclude: 6.0.0 + v8-to-istanbul: 9.1.0 + yargs: 17.7.2 + yargs-parser: 21.1.1 + dev: true + + /cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + dev: true + + /call-bind@1.0.2: + resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} + dependencies: + function-bind: 1.1.1 + get-intrinsic: 1.2.1 + dev: true + + /callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + dev: true + + /chai@4.3.8: + resolution: {integrity: sha512-vX4YvVVtxlfSZ2VecZgFUTU5qPCYsobVI2O9FmwEXBhDigYGQA6jRXCycIs1yJnnWbZ6/+a2zNIF5DfVCcJBFQ==} + engines: {node: '>=4'} + dependencies: + assertion-error: 1.1.0 + check-error: 1.0.2 + deep-eql: 4.1.3 + get-func-name: 2.0.0 + loupe: 2.3.6 + pathval: 1.1.1 + type-detect: 4.0.8 + dev: true + + /chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + dev: true + + /check-error@1.0.2: + resolution: {integrity: sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==} + dev: true + + /cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + dev: true + + /cluster-key-slot@1.1.2: + resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==} + engines: {node: '>=0.10.0'} + + /color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + dependencies: + color-name: 1.1.4 + dev: true + + /color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + dev: true + + /colours.js@3.1.2: + resolution: {integrity: sha512-SKrKq+KYIxtW+cRnnMZLxcFBOqz99DnzpFjs1iihPTfpWkQuSQ9Drg37WgRqrfXOE4aOkBoJR/CgfD09NpNBVQ==} + dev: false + + /concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + dev: true + + /convert-source-map@1.9.0: + resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} + dev: true + + /create-require@1.1.1: + resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + dev: true + + /cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + dev: true + + /debug@3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.3 + dev: true + + /debug@4.3.4: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.2 + dev: true + + /deep-eql@4.1.3: + resolution: {integrity: sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==} + engines: {node: '>=6'} + dependencies: + type-detect: 4.0.8 + dev: true + + /deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + dev: true + + /define-properties@1.2.0: + resolution: {integrity: sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==} + engines: {node: '>= 0.4'} + dependencies: + has-property-descriptors: 1.0.0 + object-keys: 1.1.1 + dev: true + + /diff-sequences@29.6.3: + resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true + + /diff@4.0.2: + resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} + engines: {node: '>=0.3.1'} + dev: true + + /dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + dependencies: + path-type: 4.0.0 + dev: true + + /doctrine@2.1.0: + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + dependencies: + esutils: 2.0.3 + dev: true + + /doctrine@3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + dependencies: + esutils: 2.0.3 + dev: true + + /emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + dev: true + + /es-abstract@1.22.1: + resolution: {integrity: sha512-ioRRcXMO6OFyRpyzV3kE1IIBd4WG5/kltnzdxSCqoP8CMGs/Li+M1uF5o7lOkZVFjDs+NLesthnF66Pg/0q0Lw==} + engines: {node: '>= 0.4'} + dependencies: + array-buffer-byte-length: 1.0.0 + arraybuffer.prototype.slice: 1.0.1 + available-typed-arrays: 1.0.5 + call-bind: 1.0.2 + es-set-tostringtag: 2.0.1 + es-to-primitive: 1.2.1 + function.prototype.name: 1.1.5 + get-intrinsic: 1.2.1 + get-symbol-description: 1.0.0 + globalthis: 1.0.3 + gopd: 1.0.1 + has: 1.0.3 + has-property-descriptors: 1.0.0 + has-proto: 1.0.1 + has-symbols: 1.0.3 + internal-slot: 1.0.5 + is-array-buffer: 3.0.2 + is-callable: 1.2.7 + is-negative-zero: 2.0.2 + is-regex: 1.1.4 + is-shared-array-buffer: 1.0.2 + is-string: 1.0.7 + is-typed-array: 1.1.12 + is-weakref: 1.0.2 + object-inspect: 1.12.3 + object-keys: 1.1.1 + object.assign: 4.1.4 + regexp.prototype.flags: 1.5.0 + safe-array-concat: 1.0.0 + safe-regex-test: 1.0.0 + string.prototype.trim: 1.2.7 + string.prototype.trimend: 1.0.6 + string.prototype.trimstart: 1.0.6 + typed-array-buffer: 1.0.0 + typed-array-byte-length: 1.0.0 + typed-array-byte-offset: 1.0.0 + typed-array-length: 1.0.4 + unbox-primitive: 1.0.2 + which-typed-array: 1.1.11 + dev: true + + /es-set-tostringtag@2.0.1: + resolution: {integrity: sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.2.1 + has: 1.0.3 + has-tostringtag: 1.0.0 + dev: true + + /es-shim-unscopables@1.0.0: + resolution: {integrity: sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==} + dependencies: + has: 1.0.3 + dev: true + + /es-to-primitive@1.2.1: + resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} + engines: {node: '>= 0.4'} + dependencies: + is-callable: 1.2.7 + is-date-object: 1.0.5 + is-symbol: 1.0.4 + dev: true + + /esbuild@0.18.20: + resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/android-arm': 0.18.20 + '@esbuild/android-arm64': 0.18.20 + '@esbuild/android-x64': 0.18.20 + '@esbuild/darwin-arm64': 0.18.20 + '@esbuild/darwin-x64': 0.18.20 + '@esbuild/freebsd-arm64': 0.18.20 + '@esbuild/freebsd-x64': 0.18.20 + '@esbuild/linux-arm': 0.18.20 + '@esbuild/linux-arm64': 0.18.20 + '@esbuild/linux-ia32': 0.18.20 + '@esbuild/linux-loong64': 0.18.20 + '@esbuild/linux-mips64el': 0.18.20 + '@esbuild/linux-ppc64': 0.18.20 + '@esbuild/linux-riscv64': 0.18.20 + '@esbuild/linux-s390x': 0.18.20 + '@esbuild/linux-x64': 0.18.20 + '@esbuild/netbsd-x64': 0.18.20 + '@esbuild/openbsd-x64': 0.18.20 + '@esbuild/sunos-x64': 0.18.20 + '@esbuild/win32-arm64': 0.18.20 + '@esbuild/win32-ia32': 0.18.20 + '@esbuild/win32-x64': 0.18.20 + dev: true + + /escalade@3.1.1: + resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} + engines: {node: '>=6'} + dev: true + + /escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + dev: true + + /eslint-import-resolver-node@0.3.9: + resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} + dependencies: + debug: 3.2.7 + is-core-module: 2.13.0 + resolve: 1.22.4 + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-module-utils@2.8.0(@typescript-eslint/parser@6.4.1)(eslint-import-resolver-node@0.3.9)(eslint@8.48.0): + resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + dependencies: + '@typescript-eslint/parser': 6.4.1(eslint@8.48.0)(typescript@5.2.2) + debug: 3.2.7 + eslint: 8.48.0 + eslint-import-resolver-node: 0.3.9 + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-plugin-import@2.28.1(@typescript-eslint/parser@6.4.1)(eslint@8.48.0): + resolution: {integrity: sha512-9I9hFlITvOV55alzoKBI+K9q74kv0iKMeY6av5+umsNwayt59fz692daGyjR+oStBQgx6nwR9rXldDev3Clw+A==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + dependencies: + '@typescript-eslint/parser': 6.4.1(eslint@8.48.0)(typescript@5.2.2) + array-includes: 3.1.6 + array.prototype.findlastindex: 1.2.2 + array.prototype.flat: 1.3.1 + array.prototype.flatmap: 1.3.1 + debug: 3.2.7 + doctrine: 2.1.0 + eslint: 8.48.0 + eslint-import-resolver-node: 0.3.9 + eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.4.1)(eslint-import-resolver-node@0.3.9)(eslint@8.48.0) + has: 1.0.3 + is-core-module: 2.13.0 + is-glob: 4.0.3 + minimatch: 3.1.2 + object.fromentries: 2.0.6 + object.groupby: 1.0.0 + object.values: 1.1.6 + semver: 6.3.1 + tsconfig-paths: 3.14.2 + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + dev: true + + /eslint-plugin-tsdoc@0.2.17: + resolution: {integrity: sha512-xRmVi7Zx44lOBuYqG8vzTXuL6IdGOeF9nHX17bjJ8+VE6fsxpdGem0/SBTmAwgYMKYB1WBkqRJVQ+n8GK041pA==} + dependencies: + '@microsoft/tsdoc': 0.14.2 + '@microsoft/tsdoc-config': 0.16.2 + dev: true + + /eslint-scope@7.2.2: + resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + dev: true + + /eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /eslint@8.48.0: + resolution: {integrity: sha512-sb6DLeIuRXxeM1YljSe1KEx9/YYeZFQWcV8Rq9HfigmdDEugjLEVEa1ozDjL6YDjBpQHPJxJzze+alxi4T3OLg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + hasBin: true + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.48.0) + '@eslint-community/regexpp': 4.8.0 + '@eslint/eslintrc': 2.1.2 + '@eslint/js': 8.48.0 + '@humanwhocodes/config-array': 0.11.10 + '@humanwhocodes/module-importer': 1.0.1 + '@nodelib/fs.walk': 1.2.8 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.3 + debug: 4.3.4 + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esquery: 1.5.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + glob-parent: 6.0.2 + globals: 13.21.0 + graphemer: 1.4.0 + ignore: 5.2.4 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + js-yaml: 4.1.0 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.3 + strip-ansi: 6.0.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + dev: true + + /espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + acorn: 8.10.0 + acorn-jsx: 5.3.2(acorn@8.10.0) + eslint-visitor-keys: 3.4.3 + dev: true + + /esquery@1.5.0: + resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} + engines: {node: '>=0.10'} + dependencies: + estraverse: 5.3.0 + dev: true + + /esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + dependencies: + estraverse: 5.3.0 + dev: true + + /estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + dev: true + + /esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + dev: true + + /fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + dev: true + + /fast-glob@3.3.1: + resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==} + engines: {node: '>=8.6.0'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.5 + dev: true + + /fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + dev: true + + /fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + dev: true + + /fastq@1.15.0: + resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} + dependencies: + reusify: 1.0.4 + dev: true + + /file-entry-cache@6.0.1: + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + flat-cache: 3.1.0 + dev: true + + /fill-range@7.0.1: + resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} + engines: {node: '>=8'} + dependencies: + to-regex-range: 5.0.1 + dev: true + + /find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + dev: true + + /flat-cache@3.1.0: + resolution: {integrity: sha512-OHx4Qwrrt0E4jEIcI5/Xb+f+QmJYNj2rrK8wiIdQOIrB9WrrJL8cjZvXdXuBTkkEwEqLycb5BeZDV1o2i9bTew==} + engines: {node: '>=12.0.0'} + dependencies: + flatted: 3.2.7 + keyv: 4.5.3 + rimraf: 3.0.2 + dev: true + + /flatted@3.2.7: + resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==} + dev: true + + /for-each@0.3.3: + resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + dependencies: + is-callable: 1.2.7 + dev: true + + /foreground-child@2.0.0: + resolution: {integrity: sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==} + engines: {node: '>=8.0.0'} + dependencies: + cross-spawn: 7.0.3 + signal-exit: 3.0.7 + dev: true + + /fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + dev: true + + /fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /function-bind@1.1.1: + resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} + dev: true + + /function.prototype.name@1.1.5: + resolution: {integrity: sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.22.1 + functions-have-names: 1.2.3 + dev: true + + /functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + dev: true + + /generic-pool@3.9.0: + resolution: {integrity: sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==} + engines: {node: '>= 4'} + + /get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + dev: true + + /get-func-name@2.0.0: + resolution: {integrity: sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==} + dev: true + + /get-intrinsic@1.2.1: + resolution: {integrity: sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==} + dependencies: + function-bind: 1.1.1 + has: 1.0.3 + has-proto: 1.0.1 + has-symbols: 1.0.3 + dev: true + + /get-symbol-description@1.0.0: + resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.1 + dev: true + + /glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + dependencies: + is-glob: 4.0.3 + dev: true + + /glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + dependencies: + is-glob: 4.0.3 + dev: true + + /glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + dev: true + + /globals@13.21.0: + resolution: {integrity: sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==} + engines: {node: '>=8'} + dependencies: + type-fest: 0.20.2 + dev: true + + /globalthis@1.0.3: + resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==} + engines: {node: '>= 0.4'} + dependencies: + define-properties: 1.2.0 + dev: true + + /globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.1 + ignore: 5.2.4 + merge2: 1.4.1 + slash: 3.0.0 + dev: true + + /gopd@1.0.1: + resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + dependencies: + get-intrinsic: 1.2.1 + dev: true + + /graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + dev: true + + /has-bigints@1.0.2: + resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} + dev: true + + /has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + dev: true + + /has-property-descriptors@1.0.0: + resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==} + dependencies: + get-intrinsic: 1.2.1 + dev: true + + /has-proto@1.0.1: + resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==} + engines: {node: '>= 0.4'} + dev: true + + /has-symbols@1.0.3: + resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + engines: {node: '>= 0.4'} + dev: true + + /has-tostringtag@1.0.0: + resolution: {integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==} + engines: {node: '>= 0.4'} + dependencies: + has-symbols: 1.0.3 + dev: true + + /has@1.0.3: + resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} + engines: {node: '>= 0.4.0'} + dependencies: + function-bind: 1.1.1 + dev: true + + /html-escaper@2.0.2: + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + dev: true + + /ignore@5.2.4: + resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} + engines: {node: '>= 4'} + dev: true + + /import-fresh@3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + dev: true + + /imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + dev: true + + /inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + dev: true + + /inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + dev: true + + /internal-slot@1.0.5: + resolution: {integrity: sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.2.1 + has: 1.0.3 + side-channel: 1.0.4 + dev: true + + /is-array-buffer@3.0.2: + resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.1 + is-typed-array: 1.1.12 + dev: true + + /is-bigint@1.0.4: + resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} + dependencies: + has-bigints: 1.0.2 + dev: true + + /is-boolean-object@1.1.2: + resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + has-tostringtag: 1.0.0 + dev: true + + /is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + dev: true + + /is-core-module@2.13.0: + resolution: {integrity: sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==} + dependencies: + has: 1.0.3 + dev: true + + /is-date-object@1.0.5: + resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + + /is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + dev: true + + /is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + dev: true + + /is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + dependencies: + is-extglob: 2.1.1 + dev: true + + /is-negative-zero@2.0.2: + resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==} + engines: {node: '>= 0.4'} + dev: true + + /is-number-object@1.0.7: + resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + + /is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + dev: true + + /is-path-inside@3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + dev: true + + /is-regex@1.1.4: + resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + has-tostringtag: 1.0.0 + dev: true + + /is-shared-array-buffer@1.0.2: + resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==} + dependencies: + call-bind: 1.0.2 + dev: true + + /is-string@1.0.7: + resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + + /is-symbol@1.0.4: + resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} + engines: {node: '>= 0.4'} + dependencies: + has-symbols: 1.0.3 + dev: true + + /is-typed-array@1.1.12: + resolution: {integrity: sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==} + engines: {node: '>= 0.4'} + dependencies: + which-typed-array: 1.1.11 + dev: true + + /is-weakref@1.0.2: + resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} + dependencies: + call-bind: 1.0.2 + dev: true + + /isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + dev: true + + /isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + dev: true + + /istanbul-lib-coverage@3.2.0: + resolution: {integrity: sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==} + engines: {node: '>=8'} + dev: true + + /istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} + dependencies: + istanbul-lib-coverage: 3.2.0 + make-dir: 4.0.0 + supports-color: 7.2.0 + dev: true + + /istanbul-reports@3.1.6: + resolution: {integrity: sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==} + engines: {node: '>=8'} + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + dev: true + + /jju@1.4.0: + resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==} + dev: true + + /js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + dependencies: + argparse: 2.0.1 + dev: true + + /json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + dev: true + + /json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + dev: true + + /json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + dev: true + + /json5@1.0.2: + resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} + hasBin: true + dependencies: + minimist: 1.2.8 + dev: true + + /jsonc-parser@3.2.0: + resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==} + dev: true + + /jsonpath-plus@7.2.0: + resolution: {integrity: sha512-zBfiUPM5nD0YZSBT/o/fbCUlCcepMIdP0CJZxM1+KgA4f2T206f6VAg9e7mX35+KlMaIc5qXW34f3BnwJ3w+RA==} + engines: {node: '>=12.0.0'} + dev: true + + /just-clone@6.2.0: + resolution: {integrity: sha512-1IynUYEc/HAwxhi3WDpIpxJbZpMCvvrrmZVqvj9EhpvbH8lls7HhdhiByjL7DkAaWlLIzpC0Xc/VPvy/UxLNjA==} + dev: true + + /keyv@4.5.3: + resolution: {integrity: sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==} + dependencies: + json-buffer: 3.0.1 + dev: true + + /levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + dev: true + + /local-pkg@0.4.3: + resolution: {integrity: sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==} + engines: {node: '>=14'} + dev: true + + /locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + dependencies: + p-locate: 5.0.0 + dev: true + + /lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + dev: true + + /loupe@2.3.6: + resolution: {integrity: sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==} + dependencies: + get-func-name: 2.0.0 + dev: true + + /lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + dependencies: + yallist: 4.0.0 + dev: true + + /magic-string@0.30.3: + resolution: {integrity: sha512-B7xGbll2fG/VjP+SWg4sX3JynwIU0mjoTc6MPpKNuIvftk6u6vqhDnk1R80b8C2GBR6ywqy+1DcKBrevBg+bmw==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + + /make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} + dependencies: + semver: 7.5.4 + dev: true + + /make-error@1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + dev: true + + /merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + dev: true + + /micromatch@4.0.5: + resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} + engines: {node: '>=8.6'} + dependencies: + braces: 3.0.2 + picomatch: 2.3.1 + dev: true + + /minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + dependencies: + brace-expansion: 1.1.11 + dev: true + + /minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + dev: true + + /mlly@1.4.1: + resolution: {integrity: sha512-SCDs78Q2o09jiZiE2WziwVBEqXQ02XkGdUy45cbJf+BpYRIjArXRJ1Wbowxkb+NaM9DWvS3UC9GiO/6eqvQ/pg==} + dependencies: + acorn: 8.10.0 + pathe: 1.1.1 + pkg-types: 1.0.3 + ufo: 1.3.0 + dev: true + + /ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + dev: true + + /ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + dev: true + + /nanoid@3.3.6: + resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + dev: true + + /natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + dev: true + + /object-inspect@1.12.3: + resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==} + dev: true + + /object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + dev: true + + /object.assign@4.1.4: + resolution: {integrity: sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + has-symbols: 1.0.3 + object-keys: 1.1.1 + dev: true + + /object.fromentries@2.0.6: + resolution: {integrity: sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.22.1 + dev: true + + /object.groupby@1.0.0: + resolution: {integrity: sha512-70MWG6NfRH9GnbZOikuhPPYzpUpof9iW2J9E4dW7FXTqPNb6rllE6u39SKwwiNh8lCwX3DDb5OgcKGiEBrTTyw==} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.22.1 + get-intrinsic: 1.2.1 + dev: true + + /object.values@1.1.6: + resolution: {integrity: sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.22.1 + dev: true + + /once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + dependencies: + wrappy: 1.0.2 + dev: true + + /optionator@0.9.3: + resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} + engines: {node: '>= 0.8.0'} + dependencies: + '@aashutoshrathi/word-wrap': 1.2.6 + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + dev: true + + /p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + dependencies: + yocto-queue: 0.1.0 + dev: true + + /p-limit@4.0.0: + resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + yocto-queue: 1.0.0 + dev: true + + /p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + dependencies: + p-limit: 3.1.0 + dev: true + + /parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + dependencies: + callsites: 3.1.0 + dev: true + + /path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + dev: true + + /path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + dev: true + + /path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + dev: true + + /path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + dev: true + + /path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + dev: true + + /pathe@1.1.1: + resolution: {integrity: sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q==} + dev: true + + /pathval@1.1.1: + resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} + dev: true + + /picocolors@1.0.0: + resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + dev: true + + /picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + dev: true + + /pkg-types@1.0.3: + resolution: {integrity: sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==} + dependencies: + jsonc-parser: 3.2.0 + mlly: 1.4.1 + pathe: 1.1.1 + dev: true + + /postcss@8.4.28: + resolution: {integrity: sha512-Z7V5j0cq8oEKyejIKfpD8b4eBy9cwW2JWPk0+fB1HOAMsfHbnAXLLS+PfVWlzMSLQaWttKDt607I0XHmpE67Vw==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.6 + picocolors: 1.0.0 + source-map-js: 1.0.2 + dev: true + + /prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + dev: true + + /pretty-format@29.6.3: + resolution: {integrity: sha512-ZsBgjVhFAj5KeK+nHfF1305/By3lechHQSMWCTl8iHSbfOm2TN5nHEtFc/+W7fAyUeCs2n5iow72gld4gW0xDw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/schemas': 29.6.3 + ansi-styles: 5.2.0 + react-is: 18.2.0 + dev: true + + /punycode@2.3.0: + resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} + engines: {node: '>=6'} + dev: true + + /queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + dev: true + + /react-is@18.2.0: + resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} + dev: true + + /redis-om@0.4.2: + resolution: {integrity: sha512-sBah+ljGQY4Zm1f9/+l7HjtIuTQH/rzIX3JSLizymbr6VlTZ2ibt+U3xYKNjIA0tv/D9toEMF7HFXHMtT+l+1A==} + engines: {node: '>= 14'} + dependencies: + jsonpath-plus: 7.2.0 + just-clone: 6.2.0 + redis: 4.6.8 + ulid: 2.3.0 + dev: true + + /redis@4.6.8: + resolution: {integrity: sha512-S7qNkPUYrsofQ0ztWlTHSaK0Qqfl1y+WMIxrzeAGNG+9iUZB4HGeBgkHxE6uJJ6iXrkvLd1RVJ2nvu6H1sAzfQ==} + dependencies: + '@redis/bloom': 1.2.0(@redis/client@1.5.9) + '@redis/client': 1.5.9 + '@redis/graph': 1.1.0(@redis/client@1.5.9) + '@redis/json': 1.0.4(@redis/client@1.5.9) + '@redis/search': 1.1.3(@redis/client@1.5.9) + '@redis/time-series': 1.0.5(@redis/client@1.5.9) + + /regexp.prototype.flags@1.5.0: + resolution: {integrity: sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + functions-have-names: 1.2.3 + dev: true + + /require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + dev: true + + /resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + dev: true + + /resolve@1.19.0: + resolution: {integrity: sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==} + dependencies: + is-core-module: 2.13.0 + path-parse: 1.0.7 + dev: true + + /resolve@1.22.4: + resolution: {integrity: sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==} + hasBin: true + dependencies: + is-core-module: 2.13.0 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + dev: true + + /reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + dev: true + + /rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + hasBin: true + dependencies: + glob: 7.2.3 + dev: true + + /rollup@3.28.1: + resolution: {integrity: sha512-R9OMQmIHJm9znrU3m3cpE8uhN0fGdXiawME7aZIpQqvpS/85+Vt1Hq1/yVIcYfOmaQiHjvXkQAoJukvLpau6Yw==} + engines: {node: '>=14.18.0', npm: '>=8.0.0'} + hasBin: true + optionalDependencies: + fsevents: 2.3.3 + dev: true + + /run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + dependencies: + queue-microtask: 1.2.3 + dev: true + + /safe-array-concat@1.0.0: + resolution: {integrity: sha512-9dVEFruWIsnie89yym+xWTAYASdpw3CJV7Li/6zBewGf9z2i1j31rP6jnY0pHEO4QZh6N0K11bFjWmdR8UGdPQ==} + engines: {node: '>=0.4'} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.1 + has-symbols: 1.0.3 + isarray: 2.0.5 + dev: true + + /safe-regex-test@1.0.0: + resolution: {integrity: sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.1 + is-regex: 1.1.4 + dev: true + + /semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + dev: true + + /semver@7.5.4: + resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} + engines: {node: '>=10'} + hasBin: true + dependencies: + lru-cache: 6.0.0 + dev: true + + /shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + dependencies: + shebang-regex: 3.0.0 + dev: true + + /shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + dev: true + + /side-channel@1.0.4: + resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.1 + object-inspect: 1.12.3 + dev: true + + /siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + dev: true + + /signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + dev: true + + /slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + dev: true + + /source-map-js@1.0.2: + resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} + engines: {node: '>=0.10.0'} + dev: true + + /stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + dev: true + + /std-env@3.4.3: + resolution: {integrity: sha512-f9aPhy8fYBuMN+sNfakZV18U39PbalgjXG3lLB9WkaYTxijru61wb57V9wxxNthXM5Sd88ETBWi29qLAsHO52Q==} + dev: true + + /string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + dev: true + + /string.prototype.trim@1.2.7: + resolution: {integrity: sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.22.1 + dev: true + + /string.prototype.trimend@1.0.6: + resolution: {integrity: sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.22.1 + dev: true + + /string.prototype.trimstart@1.0.6: + resolution: {integrity: sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.22.1 + dev: true + + /strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + dependencies: + ansi-regex: 5.0.1 + dev: true + + /strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + dev: true + + /strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + dev: true + + /strip-literal@1.3.0: + resolution: {integrity: sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==} + dependencies: + acorn: 8.10.0 + dev: true + + /supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + dependencies: + has-flag: 4.0.0 + dev: true + + /supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + dev: true + + /test-exclude@6.0.0: + resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} + engines: {node: '>=8'} + dependencies: + '@istanbuljs/schema': 0.1.3 + glob: 7.2.3 + minimatch: 3.1.2 + dev: true + + /text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + dev: true + + /tinybench@2.5.0: + resolution: {integrity: sha512-kRwSG8Zx4tjF9ZiyH4bhaebu+EDz1BOx9hOigYHlUW4xxI/wKIUQUqo018UlU4ar6ATPBsaMrdbKZ+tmPdohFA==} + dev: true + + /tinypool@0.7.0: + resolution: {integrity: sha512-zSYNUlYSMhJ6Zdou4cJwo/p7w5nmAH17GRfU/ui3ctvjXFErXXkruT4MWW6poDeXgCaIBlGLrfU6TbTXxyGMww==} + engines: {node: '>=14.0.0'} + dev: true + + /tinyspy@2.1.1: + resolution: {integrity: sha512-XPJL2uSzcOyBMky6OFrusqWlzfFrXtE0hPuMgW8A2HmaqrPo4ZQHRN/V0QXN3FSjKxpsbRrFc5LI7KOwBsT1/w==} + engines: {node: '>=14.0.0'} + dev: true + + /to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + dependencies: + is-number: 7.0.0 + dev: true + + /ts-api-utils@1.0.2(typescript@5.2.2): + resolution: {integrity: sha512-Cbu4nIqnEdd+THNEsBdkolnOXhg0I8XteoHaEKgvsxpsbWda4IsUut2c187HxywQCvveojow0Dgw/amxtSKVkQ==} + engines: {node: '>=16.13.0'} + peerDependencies: + typescript: '>=4.2.0' + dependencies: + typescript: 5.2.2 + dev: true + + /ts-node@10.9.1(@types/node@20.5.6)(typescript@5.2.2): + resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': '*' + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.9 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 20.5.6 + acorn: 8.10.0 + acorn-walk: 8.2.0 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 5.2.2 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + dev: true + + /tsconfig-paths@3.14.2: + resolution: {integrity: sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==} + dependencies: + '@types/json5': 0.0.29 + json5: 1.0.2 + minimist: 1.2.8 + strip-bom: 3.0.0 + dev: true + + /tslib@2.6.2: + resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} + dev: false + + /type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + dev: true + + /type-detect@4.0.8: + resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} + engines: {node: '>=4'} + dev: true + + /type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + dev: true + + /typed-array-buffer@1.0.0: + resolution: {integrity: sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.1 + is-typed-array: 1.1.12 + dev: true + + /typed-array-byte-length@1.0.0: + resolution: {integrity: sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + for-each: 0.3.3 + has-proto: 1.0.1 + is-typed-array: 1.1.12 + dev: true + + /typed-array-byte-offset@1.0.0: + resolution: {integrity: sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.5 + call-bind: 1.0.2 + for-each: 0.3.3 + has-proto: 1.0.1 + is-typed-array: 1.1.12 + dev: true + + /typed-array-length@1.0.4: + resolution: {integrity: sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==} + dependencies: + call-bind: 1.0.2 + for-each: 0.3.3 + is-typed-array: 1.1.12 + dev: true + + /typescript@5.2.2: + resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==} + engines: {node: '>=14.17'} + hasBin: true + dev: true + + /ufo@1.3.0: + resolution: {integrity: sha512-bRn3CsoojyNStCZe0BG0Mt4Nr/4KF+rhFlnNXybgqt5pXHNFRlqinSoQaTrGyzE4X8aHplSb+TorH+COin9Yxw==} + dev: true + + /ulid@2.3.0: + resolution: {integrity: sha512-keqHubrlpvT6G2wH0OEfSW4mquYRcbe/J8NMmveoQOjUqmo+hXtO+ORCpWhdbZ7k72UtY61BL7haGxW6enBnjw==} + hasBin: true + dev: true + + /unbox-primitive@1.0.2: + resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} + dependencies: + call-bind: 1.0.2 + has-bigints: 1.0.2 + has-symbols: 1.0.3 + which-boxed-primitive: 1.0.2 + dev: true + + /uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + dependencies: + punycode: 2.3.0 + dev: true + + /v8-compile-cache-lib@3.0.1: + resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + dev: true + + /v8-to-istanbul@9.1.0: + resolution: {integrity: sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==} + engines: {node: '>=10.12.0'} + dependencies: + '@jridgewell/trace-mapping': 0.3.19 + '@types/istanbul-lib-coverage': 2.0.4 + convert-source-map: 1.9.0 + dev: true + + /vite-node@0.34.3(@types/node@20.5.6): + resolution: {integrity: sha512-+0TzJf1g0tYXj6tR2vEyiA42OPq68QkRZCu/ERSo2PtsDJfBpDyEfuKbRvLmZqi/CgC7SCBtyC+WjTGNMRIaig==} + engines: {node: '>=v14.18.0'} + hasBin: true + dependencies: + cac: 6.7.14 + debug: 4.3.4 + mlly: 1.4.1 + pathe: 1.1.1 + picocolors: 1.0.0 + vite: 4.4.9(@types/node@20.5.6) + transitivePeerDependencies: + - '@types/node' + - less + - lightningcss + - sass + - stylus + - sugarss + - supports-color + - terser + dev: true + + /vite@4.4.9(@types/node@20.5.6): + resolution: {integrity: sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==} + engines: {node: ^14.18.0 || >=16.0.0} + hasBin: true + peerDependencies: + '@types/node': '>= 14' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + '@types/node': 20.5.6 + esbuild: 0.18.20 + postcss: 8.4.28 + rollup: 3.28.1 + optionalDependencies: + fsevents: 2.3.3 + dev: true + + /vitest@0.34.3: + resolution: {integrity: sha512-7+VA5Iw4S3USYk+qwPxHl8plCMhA5rtfwMjgoQXMT7rO5ldWcdsdo3U1QD289JgglGK4WeOzgoLTsGFu6VISyQ==} + engines: {node: '>=v14.18.0'} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@vitest/browser': '*' + '@vitest/ui': '*' + happy-dom: '*' + jsdom: '*' + playwright: '*' + safaridriver: '*' + webdriverio: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + playwright: + optional: true + safaridriver: + optional: true + webdriverio: + optional: true + dependencies: + '@types/chai': 4.3.5 + '@types/chai-subset': 1.3.3 + '@types/node': 20.5.6 + '@vitest/expect': 0.34.3 + '@vitest/runner': 0.34.3 + '@vitest/snapshot': 0.34.3 + '@vitest/spy': 0.34.3 + '@vitest/utils': 0.34.3 + acorn: 8.10.0 + acorn-walk: 8.2.0 + cac: 6.7.14 + chai: 4.3.8 + debug: 4.3.4 + local-pkg: 0.4.3 + magic-string: 0.30.3 + pathe: 1.1.1 + picocolors: 1.0.0 + std-env: 3.4.3 + strip-literal: 1.3.0 + tinybench: 2.5.0 + tinypool: 0.7.0 + vite: 4.4.9(@types/node@20.5.6) + vite-node: 0.34.3(@types/node@20.5.6) + why-is-node-running: 2.2.2 + transitivePeerDependencies: + - less + - lightningcss + - sass + - stylus + - sugarss + - supports-color + - terser + dev: true + + /which-boxed-primitive@1.0.2: + resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} + dependencies: + is-bigint: 1.0.4 + is-boolean-object: 1.1.2 + is-number-object: 1.0.7 + is-string: 1.0.7 + is-symbol: 1.0.4 + dev: true + + /which-typed-array@1.1.11: + resolution: {integrity: sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.5 + call-bind: 1.0.2 + for-each: 0.3.3 + gopd: 1.0.1 + has-tostringtag: 1.0.0 + dev: true + + /which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + dependencies: + isexe: 2.0.0 + dev: true + + /why-is-node-running@2.2.2: + resolution: {integrity: sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==} + engines: {node: '>=8'} + hasBin: true + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + dev: true + + /wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + dev: true + + /wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + dev: true + + /y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + dev: true + + /yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + + /yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + dev: true + + /yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + dependencies: + cliui: 8.0.1 + escalade: 3.1.1 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + dev: true + + /yn@3.1.1: + resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} + engines: {node: '>=6'} + dev: true + + /yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + dev: true + + /yocto-queue@1.0.0: + resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} + engines: {node: '>=12.20'} + dev: true diff --git a/spec/constants.ts b/spec/constants.ts new file mode 100644 index 0000000..fea32ea --- /dev/null +++ b/spec/constants.ts @@ -0,0 +1,97 @@ +import { Schema } from "../src" + +export const stringSchema = new Schema({ + stringField1: "string", + stringField2: { type: "string" }, + stringField3: { type: "string", default: "S" }, + stringField4: { type: "string", required: true }, + stringField5: { type: "string", default: "SS", required: true } +}) + +export const numberSchema = new Schema({ + numberField1: "number", + numberField2: { type: "number" }, + numberField3: { type: "number", default: 3 }, + numberField4: { type: "number", required: true }, + numberField5: { type: "number", default: 5, required: true } +}) + +export const booleanSchema = new Schema({ + booleanField1: "boolean", + booleanField2: { type: "boolean" }, + booleanField3: { type: "boolean", default: true }, + booleanField4: { type: "boolean", required: true }, + booleanField5: { type: "boolean", default: false, required: true } +}) + +export const textSchema = new Schema({ + textField1: "text", + textField2: { type: "text" }, + textField3: { type: "text", default: "T" }, + textField4: { type: "text", required: true }, + textField5: { type: "text", default: "TT", required: true } +}) + +export const dateSchema = new Schema({ + dateField1: "date", + dateField2: { type: "date" }, + dateField3: { type: "date", default: 874195200000 }, + dateField4: { type: "date", required: true }, + dateField5: { type: "date", default: new Date(874195200000), required: true } +}) + +export const pointSchema = new Schema({ + pointField1: "point", + pointField2: { type: "point" }, + pointField3: { type: "point", default: { longitude: 3, latitude: 3 } }, + pointField4: { type: "point", required: true }, + pointField5: { type: "point", default: { longitude: 5, latitude: 5 }, required: true } +}) + +export const arraySchema = new Schema({ + arrayField1: "array", + arrayField2: { type: "array" }, + arrayField3: { type: "array", elements: "number" }, + arrayField4: { type: "array", default: ["s"] }, + arrayField5: { type: "array", required: true }, + arrayField6: { type: "array", elements: "boolean", default: [false], required: true }, + arrayField7: { type: "array", elements: { element1: "string" } } +}) + +export const objectSchema = new Schema({ + objectField1: { + type: "object", + properties: { + nestedProperty1: "string", + nestedProperty2: "number", + nestedProperty3: "boolean", + nestedProperty4: "text", + nestedProperty5: "date", + nestedProperty6: "point", + nestedProperty7: "array", + nestedProperty9: { + type: "object", + properties: { + deep1: "string", + deep2: { + type: "object", + properties: { + nest: { + type: "object", + properties: { + finalNestBczImLazy: "array" + } + } + } + } + } + } + }, + default: { + nestedProperty9: { + deep1: "S" + } + }, + required: true + } +}) \ No newline at end of file diff --git a/spec/functional/core/fetch-hash.spec.ts b/spec/functional/core/fetch-hash.spec.ts deleted file mode 100644 index fa21aac..0000000 --- a/spec/functional/core/fetch-hash.spec.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { createClient } from 'redis' - -import { EntityKeyName, RedisConnection, Repository, Schema } from '$lib/index' - -import { createHashEntitySchema } from '../helpers/data-helper' -import { removeKeys, saveHash } from '../helpers/redis-helper' - -import { ANOTHER_ENTITY, ANOTHER_HASH, AN_EMPTY_ENTITY, AN_ENTITY, A_HASH, A_THIRD_ENTITY, A_THIRD_HASH } from '../helpers/hash-example-data' - -describe("fetch hash", () => { - - let redis: RedisConnection - let repository: Repository - let schema: Schema - - beforeAll(async () => { - redis = createClient() - await redis.connect() - await removeKeys(redis, 'fetch-hash:1', 'fetch-hash:2', 'fetch-hash:3') - await saveHash(redis, 'fetch-hash:1', A_HASH) - await saveHash(redis, 'fetch-hash:2', ANOTHER_HASH) - await saveHash(redis, 'fetch-hash:3', A_THIRD_HASH) - - schema = createHashEntitySchema('fetch-hash') - repository = new Repository(schema, redis) - }) - - afterAll(async () => { - await removeKeys(redis, 'fetch-hash:1', 'fetch-hash:2', 'fetch-hash:3') - await redis.quit() - }) - - it("fetches a single entity from Redis", async () => - expect(repository.fetch('1')).resolves.toEqual({ [EntityKeyName]: 'fetch-hash:1', ...AN_ENTITY })) - - it("fetches an empty entity from Redis", async () => - expect(repository.fetch('empty')).resolves.toEqual({ [EntityKeyName]: 'fetch-hash:empty', ...AN_EMPTY_ENTITY })) - - it("fetches multiple entities from Redis with discrete arguments", async () => { - let entities = await repository.fetch('1', '2', '3') - expect(entities).toEqual(expect.arrayContaining([ - { [EntityKeyName]: 'fetch-hash:1', ...AN_ENTITY }, - { [EntityKeyName]: 'fetch-hash:2', ...ANOTHER_ENTITY }, - { [EntityKeyName]: 'fetch-hash:3', ...A_THIRD_ENTITY } - ])) - }) - - it("fetches multiple entities from Redis with an array", async () => { - let entities = await repository.fetch(['1', '2', '3']) - expect(entities).toEqual(expect.arrayContaining([ - { [EntityKeyName]: 'fetch-hash:1', ...AN_ENTITY }, - { [EntityKeyName]: 'fetch-hash:2', ...ANOTHER_ENTITY }, - { [EntityKeyName]: 'fetch-hash:3', ...A_THIRD_ENTITY } - ])) - }) -}) diff --git a/spec/functional/core/fetch-json.spec.ts b/spec/functional/core/fetch-json.spec.ts deleted file mode 100644 index 94b9786..0000000 --- a/spec/functional/core/fetch-json.spec.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { createClient } from 'redis' - -import { EntityId, EntityKeyName, RedisConnection, Repository, Schema } from '$lib/index' - -import { createJsonEntitySchema } from '../helpers/data-helper' -import { removeKeys, saveJson } from '../helpers/redis-helper' - -import { ANOTHER_ENTITY, ANOTHER_JSON, AN_EMPTY_ENTITY, AN_ENTITY, A_JSON, A_THIRD_ENTITY, A_THIRD_JSON } from '../helpers/json-example-data' - -describe("fetch JSON", () => { - - let redis: RedisConnection - let repository: Repository - let schema: Schema - - beforeAll(async () => { - redis = createClient() - await redis.connect() - await removeKeys(redis, 'fetch-json:1', 'fetch-json:2', 'fetch-json:3') - await saveJson(redis, 'fetch-json:1', A_JSON) - await saveJson(redis, 'fetch-json:2', ANOTHER_JSON) - await saveJson(redis, 'fetch-json:3', A_THIRD_JSON) - - schema = createJsonEntitySchema('fetch-json') - repository = new Repository(schema, redis) - }) - - afterAll(async () => { - await removeKeys(redis, 'fetch-json:1', 'fetch-json:2', 'fetch-json:3') - await redis.quit() - }) - - it("fetches a single entity from Redis", async () => - expect(repository.fetch('1')).resolves.toEqual({ [EntityKeyName]: 'fetch-json:1', ...AN_ENTITY })) - - it("fetches an empty entity from Redis", async () => { - const entity = await repository.fetch('empty') - expect(entity).toEqual({ [EntityKeyName]: 'fetch-json:empty', ...AN_EMPTY_ENTITY }) - }) - - it("fetches a missing entity from Redis", async () => { - const entity = await repository.fetch('missing') - expect(entity).toEqual({ [EntityId]: 'missing', [EntityKeyName]: 'fetch-json:missing' }) - }) - - it("fetches all the entities from Redis with discrete arguments", async () => { - let entities = await repository.fetch('1', '2', '3') - expect(entities).toEqual(expect.arrayContaining([ - { [EntityKeyName]: 'fetch-json:1', ...AN_ENTITY }, - { [EntityKeyName]: 'fetch-json:2', ...ANOTHER_ENTITY }, - { [EntityKeyName]: 'fetch-json:3', ...A_THIRD_ENTITY } - ])) - }) -}) \ No newline at end of file diff --git a/spec/functional/core/remove-hash.spec.ts b/spec/functional/core/remove-hash.spec.ts deleted file mode 100644 index 472472c..0000000 --- a/spec/functional/core/remove-hash.spec.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { createClient } from 'redis' - -import { RedisConnection, Repository, Schema } from '$lib/index' - -import { createHashEntitySchema } from '../helpers/data-helper' -import { keyExists, removeKeys, saveHash } from '../helpers/redis-helper' - -import { ANOTHER_HASH, A_HASH, A_THIRD_HASH } from '../helpers/hash-example-data' - - -describe("remove hash", () => { - - let redis: RedisConnection - let repository: Repository - let schema: Schema - - beforeAll(async () => { - redis = createClient() - await redis.connect() - schema = createHashEntitySchema('remove-hash') - repository = new Repository(schema, redis) - }) - - beforeEach(async () => { - await removeKeys(redis, 'remove-hash:1', 'remove-hash:2', 'remove-hash:3') - await saveHash(redis, 'remove-hash:1', A_HASH) - await saveHash(redis, 'remove-hash:2', ANOTHER_HASH) - await saveHash(redis, 'remove-hash:3', A_THIRD_HASH) - }) - - afterAll(async () => { - await removeKeys(redis, 'remove-hash:1', 'remove-hash:2', 'remove-hash:3') - await redis.quit() - }) - - it("removes a single entity", async () => { - expect(keyExists(redis, 'remove-hash:1')).resolves.toBe(true) - await repository.remove('1') - expect(keyExists(redis, 'remove-hash:1')).resolves.toBe(false) - }) - - it("removes multiple entities with discrete arguments", async () => { - expect(keyExists(redis, 'remove-hash:1')).resolves.toBe(true) - expect(keyExists(redis, 'remove-hash:2')).resolves.toBe(true) - expect(keyExists(redis, 'remove-hash:3')).resolves.toBe(true) - - await repository.remove('1', '2', '3') - - expect(keyExists(redis, 'remove-hash:1')).resolves.toBe(false) - expect(keyExists(redis, 'remove-hash:2')).resolves.toBe(false) - expect(keyExists(redis, 'remove-hash:full')).resolves.toBe(false) - }) - - it("removes multiple entities with an array", async () => { - expect(keyExists(redis, 'remove-hash:1')).resolves.toBe(true) - expect(keyExists(redis, 'remove-hash:2')).resolves.toBe(true) - expect(keyExists(redis, 'remove-hash:3')).resolves.toBe(true) - await repository.remove([ '1', '2', '3' ]) - - expect(keyExists(redis, 'remove-hash:1')).resolves.toBe(false) - expect(keyExists(redis, 'remove-hash:2')).resolves.toBe(false) - expect(keyExists(redis, 'remove-hash:3')).resolves.toBe(false) - }) - - it("removes a non-existing entity", async () => { - expect(keyExists(redis, 'remove-hash:empty')).resolves.toBe(false) - await repository.remove('empty') - expect(keyExists(redis, 'remove-hash:empty')).resolves.toBe(false) - }) -}) diff --git a/spec/functional/core/remove-json.spec.ts b/spec/functional/core/remove-json.spec.ts deleted file mode 100644 index 251f854..0000000 --- a/spec/functional/core/remove-json.spec.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { createClient } from 'redis' - -import { RedisConnection, Repository, Schema } from '$lib/index' - -import { createJsonEntitySchema } from '../helpers/data-helper' -import { keyExists, removeKeys, saveJson } from '../helpers/redis-helper' - -import { ANOTHER_JSON, A_JSON, A_THIRD_JSON } from '../helpers/json-example-data' - -describe("remove JSON", () => { - - let redis: RedisConnection - let repository: Repository - let schema: Schema - - beforeAll(async () => { - redis = createClient() - await redis.connect() - schema = createJsonEntitySchema('remove-json') - repository = new Repository(schema, redis) - }) - - beforeEach(async () => { - await removeKeys(redis, 'remove-json:1', 'remove-json:2', 'remove-json:3') - await saveJson(redis, 'remove-json:1', A_JSON) - await saveJson(redis, 'remove-json:2', ANOTHER_JSON) - await saveJson(redis, 'remove-json:3', A_THIRD_JSON) - }) - - afterAll(async () => { - await removeKeys(redis, 'remove-json:1', 'remove-json:2', 'remove-json:3') - await redis.quit() - }) - - it("removes a single entity", async () => { - expect(keyExists(redis, 'remove-json:1')).resolves.toBe(true) - await repository.remove('1') - expect(keyExists(redis, 'remove-json:1')).resolves.toBe(false) - }) - - it("removes multiple entities with discrete arguments", async () => { - expect(keyExists(redis, 'remove-json:1')).resolves.toBe(true) - expect(keyExists(redis, 'remove-json:2')).resolves.toBe(true) - expect(keyExists(redis, 'remove-json:3')).resolves.toBe(true) - - await repository.remove('1', '2', '3') - - expect(keyExists(redis, 'remove-json:1')).resolves.toBe(false) - expect(keyExists(redis, 'remove-json:2')).resolves.toBe(false) - expect(keyExists(redis, 'remove-json:3')).resolves.toBe(false) - }) - - it("removes multiple entities with an array", async () => { - expect(keyExists(redis, 'remove-json:1')).resolves.toBe(true) - expect(keyExists(redis, 'remove-json:2')).resolves.toBe(true) - expect(keyExists(redis, 'remove-json:3')).resolves.toBe(true) - - await repository.remove([ '1', '2', '3' ]) - - expect(keyExists(redis, 'remove-json:1')).resolves.toBe(false) - expect(keyExists(redis, 'remove-json:2')).resolves.toBe(false) - expect(keyExists(redis, 'remove-json:3')).resolves.toBe(false) - }) - - it("removes a non-existing entity", async () => { - expect(keyExists(redis, 'remove-json:empty')).resolves.toBe(false) - await repository.remove('empty') - expect(keyExists(redis, 'remove-json:empty')).resolves.toBe(false) - }) -}) diff --git a/spec/functional/core/save-hash.spec.ts b/spec/functional/core/save-hash.spec.ts deleted file mode 100644 index 8568017..0000000 --- a/spec/functional/core/save-hash.spec.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { createClient } from 'redis' - -import { Entity, EntityId, EntityKeyName, RedisConnection, Repository, Schema } from '$lib/index' - -import { createHashEntitySchema } from '../helpers/data-helper' -import { fetchHashData, keyExists, removeKeys } from '../helpers/redis-helper' - -import { AN_EMPTY_ENTITY, AN_EMPTY_HASH, AN_ENTITY, A_HASH } from '../helpers/hash-example-data' - -describe("save hash", () => { - - let redis: RedisConnection - let repository: Repository - let schema: Schema - let entity: Entity - - beforeAll(async () => { - redis = createClient() - await redis.connect() - - schema = createHashEntitySchema('save-hash') - repository = new Repository(schema, redis) - }) - - beforeEach(async () => await removeKeys(redis, 'save-hash:1', 'save-hash:empty')) - afterAll(async () => { - await removeKeys(redis, 'save-hash:1', 'save-hash:empty') - await redis.quit() - }) - - describe("when saving an entity to redis", () => { - beforeEach(async () => { entity = await repository.save(AN_ENTITY) }) - - it('returns the expected entity', () => expect(entity).toEqual({ - ...AN_ENTITY, - [EntityId]: '1', - [EntityKeyName]: 'save-hash:1' - })) - - it('saves the expected Hash in Redis', async () => expect(fetchHashData(redis, 'save-hash:1')).resolves.toEqual(A_HASH)) - }) - - describe("when saving an empty entity to redis", () => { - beforeEach(async () => { entity = await repository.save(AN_EMPTY_ENTITY) }) - - it('returns the expected entity', () => expect(entity).toEqual({ - ...AN_EMPTY_ENTITY, - [EntityId]: 'empty', - [EntityKeyName]: 'save-hash:empty' - })) - - it('saves an empty Hash in Redis', async () => expect(fetchHashData(redis, 'save-hash:empty')).resolves.toEqual(AN_EMPTY_HASH)) - - it("stores no Hash at all", async () => expect(keyExists(redis, 'save-hash:empty')).resolves.toBe(false)) - }) -}) diff --git a/spec/functional/core/save-json.spec.ts b/spec/functional/core/save-json.spec.ts deleted file mode 100644 index 3ec687c..0000000 --- a/spec/functional/core/save-json.spec.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { createClient } from 'redis' - -import { Entity, EntityKeyName, EntityId, RedisConnection, Repository, Schema } from '$lib/index' - -import { createJsonEntitySchema } from '../helpers/data-helper' -import { fetchJsonData, keyExists, removeKeys } from '../helpers/redis-helper' - -import { AN_EMPTY_ENTITY, AN_EMPTY_JSON, AN_ENTITY, A_JSON } from '../helpers/json-example-data' - -describe("save JSON", () => { - - let redis: RedisConnection - let repository: Repository - let schema: Schema - let entity: Entity - - beforeAll(async () => { - redis = createClient() - await redis.connect() - - schema = createJsonEntitySchema('save-json') - repository = new Repository(schema, redis) - }) - - beforeEach(async () => await removeKeys(redis, 'save-json:1', 'save-json:empty')) - afterAll(async () => { - await removeKeys(redis, 'save-json:1', 'save-json:empty') - await redis.quit() - }) - - describe("when saving an entity to redis", () => { - beforeEach(async () => { entity = await repository.save(AN_ENTITY) }) - - it('returns the expected entity', () => expect(entity).toEqual({ - ...AN_ENTITY, - [EntityId]: '1', - [EntityKeyName]: 'save-json:1' - })) - - it('saves the expected JSON in Redis', async () => expect(fetchJsonData(redis, 'save-json:1')).resolves.toEqual(A_JSON)) - }) - - describe("when saving an empty entity to redis", () => { - beforeEach(async () => { entity = await repository.save(AN_EMPTY_ENTITY) }) - - it('returns the expected entity', () => expect(entity).toEqual({ - ...AN_EMPTY_ENTITY, - [EntityId]: 'empty', - [EntityKeyName]: 'save-json:empty' - })) - - it('saves an empty JSON in Redis', async () => expect(fetchJsonData(redis, 'save-json:empty')).resolves.toEqual(AN_EMPTY_JSON)) - it("stores an empty key", async () => expect(keyExists(redis, 'save-json:empty')).resolves.toBe(true)) - }) -}) diff --git a/spec/functional/core/update-hash.spec.ts b/spec/functional/core/update-hash.spec.ts deleted file mode 100644 index 3023759..0000000 --- a/spec/functional/core/update-hash.spec.ts +++ /dev/null @@ -1,103 +0,0 @@ -import { createClient } from 'redis' - -import { Entity, EntityId, EntityKeyName, RedisConnection, Repository, Schema } from '$lib/index' - -import { createHashEntitySchema } from '../helpers/data-helper' -import { keyExists, removeKeys, saveHash, fetchHashData } from '../helpers/redis-helper' - -import { ANOTHER_ENTITY, ANOTHER_HASH, AN_EMPTY_HASH, A_HASH } from '../helpers/hash-example-data' - -describe("update hash", () => { - - let redis: RedisConnection - let repository: Repository - let schema: Schema - let entity: Entity - let returnedEntity: Entity - - beforeAll(async () => { - redis = createClient() - await redis.connect() - - schema = createHashEntitySchema('update-hash') - repository = new Repository(schema, redis) - }) - - beforeEach(async () => { - await removeKeys(redis, 'update-hash:1') - await saveHash(redis, 'update-hash:1', A_HASH) - }) - - afterAll(async () => { - await removeKeys(redis, 'update-hash:1') - await redis.quit() - }) - - describe("when updating an Entity to Redis", () => { - beforeEach(async () => { - entity = await repository.fetch('1') - entity.aString = ANOTHER_ENTITY.aString - entity.anotherString = ANOTHER_ENTITY.anotherString - entity.someText = ANOTHER_ENTITY.someText - entity.someOtherText = ANOTHER_ENTITY.someOtherText - entity.aNumber = ANOTHER_ENTITY.aNumber - entity.anotherNumber = ANOTHER_ENTITY.anotherNumber - entity.aBoolean = ANOTHER_ENTITY.aBoolean - entity.anotherBoolean = ANOTHER_ENTITY.anotherBoolean - entity.aPoint = ANOTHER_ENTITY.aPoint - entity.anotherPoint = ANOTHER_ENTITY.anotherPoint - entity.aDate = ANOTHER_ENTITY.aDate - entity.anotherDate = ANOTHER_ENTITY.anotherDate - entity.someStrings = ANOTHER_ENTITY.someStrings - entity.someOtherStrings = ANOTHER_ENTITY.someOtherStrings - returnedEntity = await repository.save(entity) - }) - - it("returns the expected entity", () => expect(returnedEntity).toEqual({ - ...ANOTHER_ENTITY, - [EntityId]: '1', - [EntityKeyName]: 'update-hash:1' - })) - - it('saves the expected Hash in Redis', async () => expect(fetchHashData(redis, 'update-hash:1')).resolves.toEqual(ANOTHER_HASH)) - }) - - describe("when updating an entity to be completely empty", () => { - beforeEach(async () => { - entity = await repository.fetch('1') - entity.aString = null - entity.anotherString = null - entity.someText = null - entity.someOtherText = null - entity.aNumber = null - entity.anotherNumber = undefined - entity.aBoolean = undefined - entity.anotherBoolean = undefined - entity.aPoint = undefined - entity.anotherPoint = undefined - delete entity.aDate - delete entity.anotherDate - delete entity.someStrings - delete entity.someOtherStrings - returnedEntity = await repository.save(entity) - }) - - it("returns the expected entity", () => expect(returnedEntity).toEqual({ - aString: null, - anotherString: null, - someText: null, - someOtherText: null, - aNumber: null, - anotherNumber: undefined, - aBoolean: undefined, - anotherBoolean: undefined, - aPoint: undefined, - anotherPoint: undefined, - [EntityId]: '1', - [EntityKeyName]: 'update-hash:1' - })) - - it('saves an empty Hash in Redis', async () => expect(fetchHashData(redis, 'update-hash:1')).resolves.toEqual(AN_EMPTY_HASH)) - it("removes the Hash from Redis", async () => expect(keyExists(redis, 'update-hash:1')).resolves.toBe(false)) - }) -}) diff --git a/spec/functional/core/update-json.spec.ts b/spec/functional/core/update-json.spec.ts deleted file mode 100644 index 51103d4..0000000 --- a/spec/functional/core/update-json.spec.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { createClient } from 'redis' - -import { Entity, EntityId, EntityKeyName, RedisConnection, Repository, Schema } from '$lib/index' - -import { createJsonEntitySchema } from '../helpers/data-helper' -import { fetchJsonData, keyExists, removeKeys, saveJson } from '../helpers/redis-helper' - -import { ANOTHER_ENTITY, ANOTHER_JSON, AN_EMPTY_JSON, A_JSON } from '../helpers/json-example-data' - -describe("update JSON", () => { - - let redis: RedisConnection - let repository: Repository - let schema: Schema - let entity: Entity - let returnedEntity: Entity - - beforeAll(async () => { - redis = createClient() - await redis.connect() - - schema = createJsonEntitySchema('update-json') - repository = new Repository(schema, redis) - }) - - beforeEach(async () => { - await removeKeys(redis, 'update-json:1') - await saveJson(redis, 'update-json:1', A_JSON) - }) - - afterAll(async () => { - await removeKeys(redis, 'update-json:1') - await redis.quit() - }) - - describe("when updating an Entity to Redis", () => { - beforeEach(async () => { - entity = await repository.fetch('1') - entity.root = ANOTHER_ENTITY.root - entity.anotherString = ANOTHER_ENTITY.anotherString - entity.someOtherText = ANOTHER_ENTITY.someOtherText - entity.anotherNumber = ANOTHER_ENTITY.anotherNumber - entity.anotherBoolean = ANOTHER_ENTITY.anotherBoolean - entity.anotherPoint = ANOTHER_ENTITY.anotherPoint - entity.anotherDate = ANOTHER_ENTITY.anotherDate - entity.someOtherStrings = ANOTHER_ENTITY.someOtherStrings - returnedEntity = await repository.save(entity) - }) - - it("returns the expected entity", () => expect(returnedEntity).toEqual({ - ...ANOTHER_ENTITY, - [EntityId]: '1', - [EntityKeyName]: 'update-json:1' - })) - - it('create the expected JSON in Redis', async () => expect(fetchJsonData(redis, 'update-json:1')).resolves.toEqual(ANOTHER_JSON)) - }) - - describe("when updating an empty entity to Redis", () => { - beforeEach(async () => { - entity = await repository.fetch('1') - entity.root = undefined - entity.anotherString = undefined - entity.someOtherText = undefined - entity.anotherNumber = undefined - delete entity.anotherBoolean - delete entity.anotherPoint - delete entity.anotherDate - delete entity.someOtherStrings - returnedEntity = await repository.save(entity) - }) - - it("returns the expected entity", () => expect(returnedEntity).toEqual({ - root: undefined, - anotherString: undefined, - someOtherText: undefined, - anotherNumber: undefined, - [EntityId]: '1', - [EntityKeyName]: 'update-json:1' - })) - - it("creates the expected JSON", async () => expect(fetchJsonData(redis, 'update-json:1')).resolves.toEqual(AN_EMPTY_JSON)) - it("stores an empty key", async () => expect(keyExists(redis, 'update-json:1')).resolves.toBe(true)) - }) -}) diff --git a/spec/functional/demo.spec.ts b/spec/functional/demo.spec.ts deleted file mode 100644 index 0da456b..0000000 --- a/spec/functional/demo.spec.ts +++ /dev/null @@ -1,167 +0,0 @@ -import { createClient } from 'redis' - -import { Entity, EntityData, EntityId, Point, Repository, Schema } from '$lib/index' - -type BigfootSighting = Entity & { - date?: Date, - title?: string, - classification?: string[], - location?: EntityData & { - county?: string, - state?: string, - latlng?: Point - }, - temperature?: EntityData & { - high?: number, - low?: number - }, - fullMoon?: boolean -} - -describe("Demo", () => { - - it("demos", async () => { - - let entity: BigfootSighting - let entityId: string | undefined - - // establish a connection to Redis - const redis = createClient() - redis.on('error', (err) => console.log('Redis Client Error', err)) - await redis.connect() - - // define a schema - const schema: Schema = new Schema( - 'BigfootSighting', { - date: { type: 'date' }, - title: { type: 'text' }, - classification: { type: 'string[]' }, - county: { type: 'string', path: '$.location.county' }, - state: { type: 'string', path: '$.location.state' }, - latlng: { type: 'point', path: '$.location.latlng' }, - highTemp: { type: 'number', path: '$.temperature.high' }, - lowTemp: { type: 'number', path: '$.temperature.low' }, - fullMoon: { type: 'boolean' } - }, { - useStopWords: 'OFF' - }) - - /* NOTE: Just because a field is on the Schema doesn't mean it's - required. And just because it's *not* in the Schema, doesn't - mean you can't provide it. The Schema defines what is indexed - and how fields are mapped to and from Redis. */ - - // create a repository & create the index for it - const repository = new Repository(schema, redis) - await repository.createIndex() - - // write an entity and generate an entityId that is a ULID - entity = { - date: new Date('1978-10-09T00:00:00.000Z'), - title: "Bigfoot by the Walmart", - observed: "I saw Bigfoot at Walmart buying flip flops. He wears a size 17.", - classification: [ 'Class A', 'Class B' ], - location: { - latlong: { longitude: 12.34, latitude: 56.78 }, - }, - temperature: { - high: 53, - average: 47, - low: 42 - }, - fullMoon: false, - } - - entity = await repository.save(entity) - entityId = entity[EntityId] - - expect(entityId).toMatch(/^[0-9ABCDEFGHJKMNPQRSTVWXYZ]{26}$/) - - // write an entity that already has an entityId - entity = { - [EntityId]: '8086', // the provided id - date: new Date('1978-10-09T00:00:00.000Z'), - title: "Bigfoot by the Walmart", - observed: "I saw Bigfoot at Walmart buying flip flops. He wears a size 17.", - classification: [ 'Class A', 'Class B' ], - location: { - latlong: { longitude: 12.34, latitude: 56.78 }, - }, - temperature: { - high: 53, - average: 47, - low: 42 - }, - fullMoon: false, - } - - entity = await repository.save(entity) - entityId = entity[EntityId] - - expect(entityId).toBe('8086') - - // write an entity with a provided entityId - entity = { - date: new Date('1978-10-09T00:00:00.000Z'), - title: "Bigfoot by the Walmart", - observed: "I saw Bigfoot at Walmart buying flip flops. He wears a size 17.", - classification: [ 'Class A', 'Class B' ], - location: { - latlong: { longitude: 12.34, latitude: 56.78 }, - }, - temperature: { - high: 53, - average: 47, - low: 42 - }, - fullMoon: false, - } - - entity = await repository.save('8087', entity) - entityId = entity[EntityId] - - expect(entityId).toBe('8087') - - // fetch an entity - entity = await repository.fetch('8086') - - // update an entity - entity.date = new Date('1978-10-09T00:00:00.000Z') - entity.temperature!.low = 29 - entity.location!.county = "Athens" - entity.location!.state = "Ohio" - entity.location!.latlng = { longitude: 23.45, latitude: 67.89 } - - await repository.save(entity) - - // remove a couple of entities - await repository.remove('8086', '8087') - - // search for all entities - const allEntities = await repository.search().all() - - // search for some entities - const someEntities = await repository.search() - .where(s => s - .where('state').equals('OH') - .or('state').equals('KY') - ) - .and('classification').contains('Class A') - .and('latlng').inCircle(circle => circle.origin(23.45, 67.89).radius(50).miles) - .and('date').before(new Date('2000-01-01T00:00:00.000Z')) - .and('title').matchesExactly('the walmart') - .and('fullMoon').is.true().returnAll() - - - // execute a raw search - const someOtherEntities = await repository.searchRaw('@fullMoon:{true} @latlng:[23.45 67.89 50 mi]').returnAll() - - // clean up - await repository.dropIndex() - const allIds: string[] = allEntities.map(entity => entity.entityId ?? '') as string[] - await repository.remove(allIds) - - // close Redis - await redis.quit() - }) -}) diff --git a/spec/functional/helpers/data-helper.ts b/spec/functional/helpers/data-helper.ts deleted file mode 100644 index 2514ab7..0000000 --- a/spec/functional/helpers/data-helper.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { saveJson } from './redis-helper' - -import { EntityData, RedisConnection, Schema } from "$lib/index" - -export function createHashEntitySchema(prefix: string): Schema { - return new Schema(prefix, { - aString: { type: 'string', field: 'root_aString' }, - someText: { type: 'text', field: 'root_someText', sortable: true }, - aNumber: { type: 'number', field: 'root_aNumber', sortable: true }, - aBoolean: { type: 'boolean', field: 'root_aBoolean' }, - aPoint: { type: 'point', field: 'root_aPoint' }, - aDate: { type: 'date', field: 'root_aDate', sortable: true }, - someStrings: { type: 'string[]', field: 'root_someStrings', } - }, { - dataStructure: 'HASH' - }) -} - -export function createJsonEntitySchema(prefix: string): Schema { - return new Schema(prefix, { - aString: { type: 'string', path: '$.root.aString' }, - someText: { type: 'text', path: '$.root.someText', sortable: true }, - aNumber: { type: 'number', path: '$.root.aNumber', sortable: true }, - aBoolean: { type: 'boolean', path: '$.root.aBoolean' }, - aPoint: { type: 'point', path: '$.root.aPoint' }, - aDate: { type: 'date', path: '$.root.aDate', sortable: true }, - someStrings: { type: 'string[]', path: '$.root.someStrings[*]' } - }, { - dataStructure: 'JSON' - }) -} - -export async function loadTestJson(redis: RedisConnection, key: string, data: EntityData) { - - const json: any = {} - - Object.keys(data).forEach(field => { - const value = (data as any)[field] - if (value !== null) { - if (value instanceof Date) json[field] = value.getTime() / 1000 - else if (typeof value === 'object' && !Array.isArray(value)) json[field] = `${value.longitude},${value.latitude}` - else json[field] = value - } - }) - - await saveJson(redis, key, json) -} diff --git a/spec/functional/helpers/hash-example-data.ts b/spec/functional/helpers/hash-example-data.ts deleted file mode 100644 index 7e0be50..0000000 --- a/spec/functional/helpers/hash-example-data.ts +++ /dev/null @@ -1,141 +0,0 @@ -import { Entity, EntityId } from "$lib/index" - -import { RedisHashData } from "$lib/client" - -import { ANOTHER_DATE, ANOTHER_DATE_EPOCH_STRING, ANOTHER_NUMBER, ANOTHER_NUMBER_STRING, ANOTHER_POINT, ANOTHER_POINT_STRING, ANOTHER_STRING, A_DATE, A_DATE_EPOCH_STRING, A_NUMBER, A_NUMBER_STRING, A_POINT, A_POINT_STRING, A_STRING, A_THIRD_DATE, A_THIRD_DATE_EPOCH_STRING, A_THIRD_NUMBER, A_THIRD_NUMBER_STRING, A_THIRD_POINT, A_THIRD_POINT_STRING, A_THIRD_STRING, SOME_MORE_STRINGS, SOME_MORE_STRINGS_JOINED, SOME_MORE_TEXT, SOME_OTHER_STRINGS, SOME_OTHER_STRINGS_JOINED, SOME_OTHER_TEXT, SOME_STRINGS, SOME_STRINGS_JOINED, SOME_TEXT } from "../../helpers/example-data" - -export const AN_ENTITY: Entity = { - [EntityId]: '1', - // in schema - aString: A_STRING, - someText: SOME_TEXT, - aNumber: A_NUMBER, - aBoolean: true, - aPoint: A_POINT, - aDate: A_DATE, - someStrings: SOME_STRINGS, - // not in schema - anotherString: ANOTHER_STRING, - someOtherText: SOME_OTHER_TEXT, - anotherNumber: ANOTHER_NUMBER_STRING, - anotherBoolean: '0', - anotherPoint: ANOTHER_POINT_STRING, - anotherDate: ANOTHER_DATE_EPOCH_STRING, - someOtherStrings: SOME_OTHER_STRINGS_JOINED -} - -export const A_HASH: RedisHashData = { - // in schema - root_aString: A_STRING, - root_someText: SOME_TEXT, - root_aNumber: A_NUMBER_STRING, - root_aBoolean: '1', - root_aPoint: A_POINT_STRING, - root_aDate: A_DATE_EPOCH_STRING, - root_someStrings: SOME_STRINGS_JOINED, - // not in schema - anotherString: ANOTHER_STRING, - someOtherText: SOME_OTHER_TEXT, - anotherNumber: ANOTHER_NUMBER_STRING, - anotherBoolean: '0', - anotherPoint: ANOTHER_POINT_STRING, - anotherDate: ANOTHER_DATE_EPOCH_STRING, - someOtherStrings: SOME_OTHER_STRINGS_JOINED -} - -export const ANOTHER_ENTITY: Entity = { - [EntityId]: '2', - // in schema - aString: ANOTHER_STRING, - someText: SOME_OTHER_TEXT, - aNumber: ANOTHER_NUMBER, - aBoolean: true, - aPoint: ANOTHER_POINT, - aDate: ANOTHER_DATE, - someStrings: SOME_OTHER_STRINGS, - // not in schema - anotherString: A_THIRD_STRING, - someOtherText: SOME_MORE_TEXT, - anotherNumber: A_THIRD_NUMBER_STRING, - anotherBoolean: '1', - anotherPoint: A_THIRD_POINT_STRING, - anotherDate: A_THIRD_DATE_EPOCH_STRING, - someOtherStrings: SOME_MORE_STRINGS_JOINED -} - -export const ANOTHER_HASH: RedisHashData = { - // in schema - root_aString: ANOTHER_STRING, - root_someText: SOME_OTHER_TEXT, - root_aNumber: ANOTHER_NUMBER_STRING, - root_aBoolean: '1', - root_aPoint: ANOTHER_POINT_STRING, - root_aDate: ANOTHER_DATE_EPOCH_STRING, - root_someStrings: SOME_OTHER_STRINGS_JOINED, - // not in schema - anotherString: A_THIRD_STRING, - someOtherText: SOME_MORE_TEXT, - anotherNumber: A_THIRD_NUMBER_STRING, - anotherBoolean: '1', - anotherPoint: A_THIRD_POINT_STRING, - anotherDate: A_THIRD_DATE_EPOCH_STRING, - someOtherStrings: SOME_MORE_STRINGS_JOINED -} - -export const A_THIRD_ENTITY: Entity = { - [EntityId]: '3', - // in schema - aString: A_THIRD_STRING, - someText: SOME_MORE_TEXT, - aNumber: A_THIRD_NUMBER, - aBoolean: false, - aPoint: A_THIRD_POINT, - aDate: A_THIRD_DATE, - someStrings: SOME_MORE_STRINGS, - // not in schema - anotherString: A_STRING, - someOtherText: SOME_TEXT, - anotherNumber: A_NUMBER_STRING, - anotherBoolean: '0', - anotherPoint: A_POINT_STRING, - anotherDate: A_DATE_EPOCH_STRING, - someOtherStrings: SOME_STRINGS_JOINED -} - -export const A_THIRD_HASH: RedisHashData = { - // in schema - root_aString: A_THIRD_STRING, - root_someText: SOME_MORE_TEXT, - root_aNumber: A_THIRD_NUMBER_STRING, - root_aBoolean: '0', - root_aPoint: A_THIRD_POINT_STRING, - root_aDate: A_THIRD_DATE_EPOCH_STRING, - root_someStrings: SOME_MORE_STRINGS_JOINED, - // not in schema - anotherString: A_STRING, - someOtherText: SOME_TEXT, - anotherNumber: A_NUMBER_STRING, - anotherBoolean: '0', - anotherPoint: A_POINT_STRING, - anotherDate: A_DATE_EPOCH_STRING, - someOtherStrings: SOME_STRINGS_JOINED -} - -export const AN_EMPTY_ENTITY: Entity = { - [EntityId]: 'empty' -} - -export const AN_EMPTY_HASH: RedisHashData = {} - -export const AN_ESCAPED_ENTITY: Entity = { - [EntityId]: 'escaped', - aString: "foo ,.<>{}[]\"':;!@#$%^()-+=~& bar", - someText: "zany ,.<>{}[]\"':;!@#$%^&*()-+=~| fox", - someStrings: ['alfa ,.<>{}[]"\':;!@#$%^&()-+=~ bravo', 'charlie delta'] -} - -export const AN_ESCAPED_HASH: RedisHashData = { - root_aString: "foo ,.<>{}[]\"':;!@#$%^()-+=~& bar", - root_someText: "zany ,.<>{}[]\"':;!@#$%^&*()-+=~| fox", - root_someStrings: ['alfa ,.<>{}[]"\':;!@#$%^&()-+=~ bravo', 'charlie delta'].join('|') -} diff --git a/spec/functional/helpers/json-example-data.ts b/spec/functional/helpers/json-example-data.ts deleted file mode 100644 index 6921b95..0000000 --- a/spec/functional/helpers/json-example-data.ts +++ /dev/null @@ -1,157 +0,0 @@ -import { Entity, EntityId } from "$lib/index" - -import { RedisJsonData } from "$lib/client" - -import { ANOTHER_DATE, ANOTHER_DATE_EPOCH, ANOTHER_NUMBER, ANOTHER_POINT, ANOTHER_POINT_STRING, ANOTHER_STRING, A_DATE, A_DATE_EPOCH, A_NUMBER, A_POINT, A_POINT_STRING, A_STRING, A_THIRD_DATE, A_THIRD_DATE_EPOCH, A_THIRD_NUMBER, A_THIRD_POINT, A_THIRD_POINT_STRING, A_THIRD_STRING, SOME_MORE_STRINGS, SOME_MORE_TEXT, SOME_OTHER_STRINGS, SOME_OTHER_TEXT, SOME_STRINGS, SOME_TEXT } from "../../helpers/example-data" - -export const AN_ENTITY: Entity = { - [EntityId]: '1', - // in schema - root: { - aString: A_STRING, - someText: SOME_TEXT, - aNumber: A_NUMBER, - aBoolean: true, - aPoint: A_POINT, - aDate: A_DATE, - someStrings: SOME_STRINGS - }, - // not in schema - anotherString: ANOTHER_STRING, - someOtherText: SOME_OTHER_TEXT, - anotherNumber: ANOTHER_NUMBER, - anotherBoolean: false, - anotherPoint: ANOTHER_POINT, - anotherDate: ANOTHER_DATE_EPOCH, - someOtherStrings: SOME_OTHER_STRINGS -} - -export const A_JSON: RedisJsonData = { - // in schema - root: { - aString: A_STRING, - someText: SOME_TEXT, - aNumber: A_NUMBER, - aBoolean: true, - aPoint: A_POINT_STRING, - aDate: A_DATE_EPOCH, - someStrings: SOME_STRINGS - }, - // not in schema - anotherString: ANOTHER_STRING, - someOtherText: SOME_OTHER_TEXT, - anotherNumber: ANOTHER_NUMBER, - anotherBoolean: false, - anotherPoint: ANOTHER_POINT, - anotherDate: ANOTHER_DATE_EPOCH, - someOtherStrings: SOME_OTHER_STRINGS -} - -export const ANOTHER_ENTITY: Entity = { - [EntityId]: '2', - // in schema - root: { - aString: ANOTHER_STRING, - someText: SOME_OTHER_TEXT, - aNumber: ANOTHER_NUMBER, - aBoolean: true, - aPoint: ANOTHER_POINT, - aDate: ANOTHER_DATE, - someStrings: SOME_OTHER_STRINGS - }, - // not in schema - anotherString: A_THIRD_STRING, - someOtherText: SOME_MORE_TEXT, - anotherNumber: A_THIRD_NUMBER, - anotherBoolean: true, - anotherPoint: A_THIRD_POINT, - anotherDate: A_THIRD_DATE_EPOCH, - someOtherStrings: SOME_MORE_STRINGS -} - -export const ANOTHER_JSON: RedisJsonData = { - // in schema - root: { - aString: ANOTHER_STRING, - someText: SOME_OTHER_TEXT, - aNumber: ANOTHER_NUMBER, - aBoolean: true, - aPoint: ANOTHER_POINT_STRING, - aDate: ANOTHER_DATE_EPOCH, - someStrings: SOME_OTHER_STRINGS - }, - // not in schema - anotherString: A_THIRD_STRING, - someOtherText: SOME_MORE_TEXT, - anotherNumber: A_THIRD_NUMBER, - anotherBoolean: true, - anotherPoint: A_THIRD_POINT, - anotherDate: A_THIRD_DATE_EPOCH, - someOtherStrings: SOME_MORE_STRINGS -} - -export const A_THIRD_ENTITY: Entity = { - [EntityId]: '3', - // in schema - root: { - aString: A_THIRD_STRING, - someText: SOME_MORE_TEXT, - aNumber: A_THIRD_NUMBER, - aBoolean: false, - aPoint: A_THIRD_POINT, - aDate: A_THIRD_DATE, - someStrings: SOME_MORE_STRINGS - }, - // not in schema - anotherString: A_STRING, - someOtherText: SOME_TEXT, - anotherNumber: A_NUMBER, - anotherBoolean: false, - anotherPoint: A_POINT, - anotherDate: A_DATE_EPOCH, - someOtherStrings: SOME_STRINGS -} - -export const A_THIRD_JSON: RedisJsonData = { - // in schema - root: { - aString: A_THIRD_STRING, - someText: SOME_MORE_TEXT, - aNumber: A_THIRD_NUMBER, - aBoolean: false, - aPoint: A_THIRD_POINT_STRING, - aDate: A_THIRD_DATE_EPOCH, - someStrings: SOME_MORE_STRINGS - }, - // not in schema - anotherString: A_STRING, - someOtherText: SOME_TEXT, - anotherNumber: A_NUMBER, - anotherBoolean: false, - anotherPoint: A_POINT, - anotherDate: A_DATE_EPOCH, - someOtherStrings: SOME_STRINGS -} - -export const AN_EMPTY_ENTITY: Entity = { - [EntityId]: 'empty' -} - -export const AN_EMPTY_JSON: RedisJsonData = {} - -export const AN_ESCAPED_ENTITY: Entity = { - [EntityId]: 'escaped', - root: { - aString: "foo ,.<>{}[]\"':;!@#$%^()-+=~& bar", - someText: "zany ,.<>{}[]\"':;!@#$%^&*()-+=~| fox", - someStrings: ['alfa ,.<>{}[]"\':;!@#$%^&()-+=~ bravo', 'charlie delta'] - } -} - -export const AN_ESCAPED_JSON: RedisJsonData = { - root: { - aString: "foo ,.<>{}[]\"':;!@#$%^()-+=~& bar", - someText: "zany ,.<>{}[]\"':;!@#$%^&*()-+=~| fox", - someStrings: ['alfa ,.<>{}[]"\':;!@#$%^&()-+=~ bravo', 'charlie delta'] - } -} diff --git a/spec/functional/helpers/redis-helper.ts b/spec/functional/helpers/redis-helper.ts deleted file mode 100644 index ca6f5d9..0000000 --- a/spec/functional/helpers/redis-helper.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { RedisConnection, RedisHashData, RedisJsonData } from "$lib/client" - -export async function sleep(ms: number) { - return new Promise(resolve => setTimeout(resolve, ms)); -} - -export async function removeKeys(redis: RedisConnection, ...keys: string[]) { - for (const key of keys) await redis.del(key) -} - -export async function saveHash(redis: RedisConnection, key: string, fieldsAndValues: RedisHashData) { - await redis.hSet(key, fieldsAndValues) -} - -export async function saveJson(redis: RedisConnection, key: string, json: RedisJsonData) { - await redis.json.set(key, '$', json) -} - -export async function keyExists(redis: RedisConnection, key: string): Promise { - const exists = await redis.exists(key) - return !!exists -} - -export async function fetchJsonData(redis: RedisConnection, key: string): Promise { - const results = await redis.json.get(key, { path: '$' }) - return results === null ? null : (results as RedisJsonData)[0] -} - -export async function fetchHashKeys(redis: RedisConnection, key: string): Promise { - return await redis.hKeys(key) -} - -export async function fetchHashFields(redis: RedisConnection, key: string, ...fields: string[]): Promise { - return await redis.hmGet(key, fields) -} - -export async function fetchHashData(redis: RedisConnection, key: string): Promise { - return await redis.hGetAll(key) -} - -export async function fetchIndexInfo(redis: RedisConnection, indexName: string): Promise { - return await redis.ft.info(indexName) -} - -export async function fetchIndexHash(redis: RedisConnection, indexHashName: string): Promise { - return await redis.get(indexHashName) -} diff --git a/spec/functional/search/create-and-drop-index-on-hash.spec.ts b/spec/functional/search/create-and-drop-index-on-hash.spec.ts deleted file mode 100644 index b3eda86..0000000 --- a/spec/functional/search/create-and-drop-index-on-hash.spec.ts +++ /dev/null @@ -1,118 +0,0 @@ -import { createClient } from 'redis' - -import { RedisConnection, Repository, Schema } from '$lib/index' - -import { createHashEntitySchema } from '../helpers/data-helper' -import { fetchIndexHash, fetchIndexInfo, removeKeys } from '../helpers/redis-helper' - -const expected = [ - { identifier: 'root_aString', attribute: 'aString', type: 'TAG', SEPARATOR: '|' }, - { identifier: 'root_someText', attribute: 'someText', type: 'TEXT', WEIGHT: '1', SORTABLE: undefined }, - { identifier: 'root_aNumber', attribute: 'aNumber', type: 'NUMERIC', SORTABLE: undefined }, - { identifier: 'root_aBoolean', attribute: 'aBoolean', type: 'TAG', SEPARATOR: ',' }, - { identifier: 'root_aPoint', attribute: 'aPoint', type: 'GEO' }, - { identifier: 'root_aDate', attribute: 'aDate', type: 'NUMERIC', SORTABLE: undefined }, - { identifier: 'root_someStrings', attribute: 'someStrings', type: 'TAG', SEPARATOR: '|' }, -] - -describe("create and drop index on hash", () => { - - let redis: RedisConnection - let repository: Repository - let schema: Schema - let indexInfo: any - let indexHash: string | null - - beforeAll(async () => { - redis = createClient() - await redis.connect() - - schema = createHashEntitySchema('create-drop-hash') - repository = new Repository(schema, redis) - }) - - afterAll(async () => { - await removeKeys(redis, 'create-drop-hash:index:hash', 'create-drop-hash-changed:index:hash') - await repository.dropIndex() - await redis.quit() - }) - - describe("when the index is created", () => { - beforeEach(async () => { - await removeKeys(redis, 'create-drop-hash:index:hash', 'create-drop-hash-changed:index:hash') - await repository.createIndex() - indexInfo = await fetchIndexInfo(redis, 'create-drop-hash:index') - indexHash = await fetchIndexHash(redis, 'create-drop-hash:index:hash') - }) - - it("has the expected name", () => { - expect(indexInfo.indexName).toBe('create-drop-hash:index') - }) - - it("has the expected key type", () => { - expect(indexInfo.indexDefinition.key_type).toBe('HASH') - }) - - it("has the expected prefixes", () => { - expect(indexInfo.indexDefinition.prefixes).toEqual(['create-drop-hash:']) - }) - - it("has the expected hash", () => { - expect(indexHash).toBe("Kw31lMY+/x+l+GB0RLuUpptoFCY=") - }) - - it("has the expected fields", () => { - expect(indexInfo.attributes).toHaveLength(7) - expect(indexInfo.attributes).toEqual(expected) - }) - - describe("and then the index is dropped", () => { - beforeEach(async () => await repository.dropIndex()) - - it("the index no longer exists", async () => { - expect(async () => await fetchIndexInfo(redis, 'create-drop-hash:index')) - .rejects.toThrow("Unknown Index name") - }) - - it("the index hash no longer exists", async () => { - let hash = await fetchIndexHash(redis, 'create-drop-hash:index:hash') - expect(hash).toBeNull() - }) - }) - - describe("and then the index is recreated but not changed", () => { - beforeEach(async () => { - await repository.createIndex() - indexInfo = await fetchIndexInfo(redis, 'create-drop-hash:index') - indexHash = await fetchIndexHash(redis, 'create-drop-hash:index:hash') - }) - - it("still has the expected attributes", () => { - expect(indexInfo.indexName).toBe('create-drop-hash:index') - expect(indexInfo.indexDefinition.key_type).toBe('HASH') - expect(indexInfo.indexDefinition.prefixes).toEqual(['create-drop-hash:']) - expect(indexHash).toBe("Kw31lMY+/x+l+GB0RLuUpptoFCY=") - expect(indexInfo.attributes).toHaveLength(7) - expect(indexInfo.attributes).toEqual(expected) - }) - }) - - describe("and then the index is changed", () => { - beforeEach(async () => { - schema = createHashEntitySchema('create-drop-hash-changed') - repository = new Repository(schema, redis) - - await repository.createIndex() - indexInfo = await fetchIndexInfo(redis, 'create-drop-hash-changed:index') - indexHash = await fetchIndexHash(redis, 'create-drop-hash-changed:index:hash') - }) - - it("has new attributes", () => { - expect(indexInfo.indexName).toBe('create-drop-hash-changed:index') - expect(indexInfo.indexDefinition.key_type).toBe('HASH') - expect(indexInfo.indexDefinition.prefixes).toEqual(['create-drop-hash-changed:']) - expect(indexHash).toBe("Sbpbl+ZRM8GhzNbfqpJXgOlwYfo=") - }) - }) - }) -}) diff --git a/spec/functional/search/create-and-drop-index-on-json.spec.ts b/spec/functional/search/create-and-drop-index-on-json.spec.ts deleted file mode 100644 index 526a63f..0000000 --- a/spec/functional/search/create-and-drop-index-on-json.spec.ts +++ /dev/null @@ -1,118 +0,0 @@ -import { createClient } from 'redis' - -import { RedisConnection, Repository, Schema } from '$lib/index' - -import { createJsonEntitySchema } from '../helpers/data-helper' -import { fetchIndexHash, fetchIndexInfo, removeKeys } from '../helpers/redis-helper' - -const expected = [ - { identifier: '$.root.aString', attribute: 'aString', type: 'TAG', SEPARATOR: '|' }, - { identifier: '$.root.someText', attribute: 'someText', type: 'TEXT', WEIGHT: '1', SORTABLE: 'UNF' }, - { identifier: '$.root.aNumber', attribute: 'aNumber', type: 'NUMERIC', SORTABLE: 'UNF' }, - { identifier: '$.root.aBoolean', attribute: 'aBoolean', type: 'TAG', SEPARATOR: '' }, - { identifier: '$.root.aPoint', attribute: 'aPoint', type: 'GEO' }, - { identifier: '$.root.aDate', attribute: 'aDate', type: 'NUMERIC', SORTABLE: 'UNF' }, - { identifier: '$.root.someStrings[*]', attribute: 'someStrings', type: 'TAG', SEPARATOR: '|' } -] - -describe("create and drop index on JSON", () => { - - let redis: RedisConnection - let repository: Repository - let schema: Schema - let indexInfo: any - let indexHash: string | null - - beforeAll(async () => { - redis = createClient() - await redis.connect() - - schema = createJsonEntitySchema('create-drop-json') - repository = new Repository(schema, redis) - }) - - afterAll(async () => { - await removeKeys(redis, 'create-drop-json:index:hash', 'create-drop-json-changed:index:hash') - await repository.dropIndex() - await redis.quit() - }) - - describe("when the index is created", () => { - beforeEach(async () => { - await removeKeys(redis, 'create-drop-json:index:hash', 'create-drop-json-changed:index:hash') - await repository.createIndex() - indexInfo = await fetchIndexInfo(redis, 'create-drop-json:index') - indexHash = await fetchIndexHash(redis, 'create-drop-json:index:hash') - }) - - it("has the expected name", () => { - expect(indexInfo.indexName).toBe('create-drop-json:index') - }) - - it("has the expected key type", () => { - expect(indexInfo.indexDefinition.key_type).toBe('JSON') - }) - - it("has the expected prefixes", () => { - expect(indexInfo.indexDefinition.prefixes).toEqual(['create-drop-json:']) - }) - - it("has the expected hash", () => { - expect(indexHash).toBe("o74BCR4MFyeWwukz8UvLjyIx/mQ=") - }) - - it("has the expected fields", () => { - expect(indexInfo.attributes).toHaveLength(7) - expect(indexInfo.attributes).toEqual(expected) - }) - - describe("when the index is dropped", () => { - beforeEach(async () => await repository.dropIndex()) - - it("the index no longer exists", () => { - expect(async () => await fetchIndexInfo(redis, 'create-drop-json:index')) - .rejects.toThrow("Unknown Index name") - }) - - it("the index hash no longer exists", async () => { - let hash = await fetchIndexHash(redis, 'create-drop-json:index:hash') - expect(hash).toBeNull() - }) - }) - - describe("and then the index is recreated but not changed", () => { - beforeEach(async () => { - await repository.createIndex() - indexInfo = await fetchIndexInfo(redis, 'create-drop-json:index') - indexHash = await fetchIndexHash(redis, 'create-drop-json:index:hash') - }) - - it("still has the expected attributes", () => { - expect(indexInfo.indexName).toBe('create-drop-json:index') - expect(indexInfo.indexDefinition.key_type).toBe('JSON') - expect(indexInfo.indexDefinition.prefixes).toEqual(['create-drop-json:']) - expect(indexHash).toBe("o74BCR4MFyeWwukz8UvLjyIx/mQ=") - expect(indexInfo.attributes).toHaveLength(7) - expect(indexInfo.attributes).toEqual(expected) - }) - }) - - describe("and then the index is changed", () => { - beforeEach(async () => { - schema = createJsonEntitySchema('create-drop-json-changed') - repository = new Repository(schema, redis) - - await repository.createIndex() - indexInfo = await fetchIndexInfo(redis, 'create-drop-json-changed:index') - indexHash = await fetchIndexHash(redis, 'create-drop-json-changed:index:hash') - }) - - it("has new attributes", () => { - expect(indexInfo.indexName).toBe('create-drop-json-changed:index') - expect(indexInfo.indexDefinition.key_type).toBe('JSON') - expect(indexInfo.indexDefinition.prefixes).toEqual(['create-drop-json-changed:']) - expect(indexHash).toBe("a1Rv7/FRuFW/jQ6KJxp2jgpOH1I=") - }) - }) - }) -}) diff --git a/spec/functional/search/drop-missing-index.spec.ts b/spec/functional/search/drop-missing-index.spec.ts deleted file mode 100644 index e68da05..0000000 --- a/spec/functional/search/drop-missing-index.spec.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { createClient } from 'redis' - -import { RedisConnection, Repository, Schema } from '$lib/index' - -import { createHashEntitySchema } from '../helpers/data-helper' -import { fetchIndexInfo } from '../helpers/redis-helper' - -describe("drop missing index on hash", () => { - - let redis: RedisConnection - let repository: Repository - let schema: Schema - - beforeAll(async () => { - redis = createClient() - await redis.connect() - - schema = createHashEntitySchema('drop-missing') - repository = new Repository(schema, redis) - }) - - afterAll(async () => { - await redis.quit() - }) - - describe("when the index is dropped", () => { - beforeEach(async () => { - await repository.dropIndex() - }) - - it("the index still doesn't exists", () => { - expect(async () => await fetchIndexInfo(redis, 'drop-missing:index')) - .rejects.toThrow("Unknown Index name") - }) - }) -}) diff --git a/spec/functional/search/search-hash.spec.ts b/spec/functional/search/search-hash.spec.ts deleted file mode 100644 index f0ea3dd..0000000 --- a/spec/functional/search/search-hash.spec.ts +++ /dev/null @@ -1,219 +0,0 @@ -import '../../helpers/custom-matchers' - -import { createClient } from 'redis' - -import { Entity, EntityKeyName, Repository, RedisConnection, Schema, SearchError } from '$lib/index' - -import { createHashEntitySchema } from '../helpers/data-helper' -import { removeKeys, saveHash, sleep } from '../helpers/redis-helper' - -import { ANOTHER_ENTITY, ANOTHER_HASH, AN_ENTITY, AN_ESCAPED_ENTITY, AN_ESCAPED_HASH, A_HASH, A_THIRD_ENTITY, A_THIRD_HASH } from '../helpers/hash-example-data' -import { A_DATE, A_POINT } from '../../helpers/example-data' - -describe("search for hashes", () => { - - let redis: RedisConnection - let repository: Repository - let schema: Schema - let entities: Entity[] - - beforeAll(async () => { - redis = createClient() - await redis.connect() - - await removeKeys(redis, 'search-hash:1', 'search-hash:2', 'search-hash:3', 'search-hash:escaped') - await saveHash(redis, 'search-hash:1', A_HASH) - await saveHash(redis, 'search-hash:2', ANOTHER_HASH) - await saveHash(redis, 'search-hash:3', A_THIRD_HASH) - await saveHash(redis, 'search-hash:escaped', AN_ESCAPED_HASH) - - schema = createHashEntitySchema('search-hash') - repository = new Repository(schema, redis) - - await repository.createIndex() - await sleep(50) // Yuck! Gotta wait for RediSearch to index everything. - }) - - afterAll(async () => { - await removeKeys(redis, 'search-hash:1', 'search-hash:2', 'search-hash:3', 'search-hash:escaped') - await repository.dropIndex() - await redis.quit() - }) - - it("performs a wildcard search", async () => { - entities = await repository.search().returnAll() - - expect(entities).toHaveLength(4) - expect(entities).toEqual(expect.arrayContaining([ - { [EntityKeyName]: 'search-hash:1', ...AN_ENTITY }, - { [EntityKeyName]: 'search-hash:2', ...ANOTHER_ENTITY }, - { [EntityKeyName]: 'search-hash:3', ...A_THIRD_ENTITY }, - { [EntityKeyName]: 'search-hash:escaped', ...AN_ESCAPED_ENTITY }, - ])) - }) - - it("performs a sorted search", async () => { - entities = await repository.search().sortAscending('aNumber').returnAll() - - expect(entities).toHaveLength(4) - expect(entities).toEqual([ - { [EntityKeyName]: 'search-hash:3', ...A_THIRD_ENTITY }, - { [EntityKeyName]: 'search-hash:2', ...ANOTHER_ENTITY }, - { [EntityKeyName]: 'search-hash:1', ...AN_ENTITY }, - { [EntityKeyName]: 'search-hash:escaped', ...AN_ESCAPED_ENTITY } - ]) - }) - - it("performs a paginated search", async () => { - entities = await repository.search().returnAll({ pageSize: 2 }) - - expect(entities).toHaveLength(4) - expect(entities).toEqual(expect.arrayContaining([ - { [EntityKeyName]: 'search-hash:1', ...AN_ENTITY }, - { [EntityKeyName]: 'search-hash:2', ...ANOTHER_ENTITY }, - { [EntityKeyName]: 'search-hash:3', ...A_THIRD_ENTITY }, - { [EntityKeyName]: 'search-hash:escaped', ...AN_ESCAPED_ENTITY } - ])) - }) - - it("performs a raw search", async () => { - entities = await repository.searchRaw('@aString:{foo} @aNumber:[42 42]').returnAll() - - expect(entities).toHaveLength(1) - expect(entities).toEqual(expect.arrayContaining([ - { [EntityKeyName]: 'search-hash:1', ...AN_ENTITY } - ])) - }) - - it("searches a string", async () => { - entities = await repository.search().where('aString').eq('foo').returnAll() - - expect(entities).toHaveLength(1) - expect(entities).toEqual(expect.arrayContaining([ - { [EntityKeyName]: 'search-hash:1', ...AN_ENTITY } - ])) - }) - - it("searches a text", async () => { - entities = await repository.search().where('someText').matches('brown quick').returnAll() - - expect(entities).toHaveLength(1) - expect(entities).toEqual(expect.arrayContaining([ - { [EntityKeyName]: 'search-hash:1', ...AN_ENTITY } - ])) - }) - - it("searches a text with wildcards", async () => { - entities = await repository.search().where('someText').matches('br*').returnAll() - - expect(entities).toHaveLength(1) - expect(entities).toEqual([ { [EntityKeyName]: 'search-hash:1', ...AN_ENTITY } ]) - }) - - it("searches a text with an exact match", async () => { - entities = await repository.search().where('someText').exactly.matches('quick brown').returnAll() - - expect(entities).toHaveLength(1) - expect(entities).toEqual([ { [EntityKeyName]: 'search-hash:1', ...AN_ENTITY } ]) - }) - - it("searches a text with stop words", async () => { - entities = await repository.search().where('someText').matches('brown quick the').returnAll() - - expect(entities).toHaveLength(1) - expect(entities).toEqual([ { [EntityKeyName]: 'search-hash:1', ...AN_ENTITY } ]) - }) - - it("throw an error when searching a text with an exact match and stop words", async () => { - expect.assertions(2) - try { - await repository.search().where('someText').exactly.matches('the quick brown').returnAll() - } catch (error) { - const err = error as Error - expect(err).toBeInstanceOf(SearchError) - expect(err.message).toEqual(`The query to RediSearch had a syntax error: "Syntax error at offset 12 near the".\nThis is often the result of using a stop word in the query. Either change the query to not use a stop word or change the stop words in the schema definition. You can check the RediSearch source for the default stop words at: https://github.com/RediSearch/RediSearch/blob/master/src/stopwords.h.`) - } - }) - - it("searches a number", async () => { - entities = await repository.search().where('aNumber').lte(23).returnAll() - - expect(entities).toHaveLength(2) - expect(entities).toEqual(expect.arrayContaining([ - { [EntityKeyName]: 'search-hash:2', ...ANOTHER_ENTITY }, - { [EntityKeyName]: 'search-hash:3', ...A_THIRD_ENTITY } - ])) - }) - - it("searches a boolean", async () => { - entities = await repository.search().where('aBoolean').true().returnAll() - - expect(entities).toHaveLength(2) - expect(entities).toEqual(expect.arrayContaining([ - { [EntityKeyName]: 'search-hash:1', ...AN_ENTITY }, - { [EntityKeyName]: 'search-hash:2', ...ANOTHER_ENTITY } - ])) - }) - - it("searches a point", async () => { - entities = await repository.search() - .where('aPoint').inCircle(circle => circle.origin(A_POINT).radius(10).meters) - .returnAll() - - expect(entities).toHaveLength(1) - expect(entities).toEqual([ { [EntityKeyName]: 'search-hash:1', ...AN_ENTITY } ]) - }) - - it("searches a date", async () => { - entities = await repository.search().where('aDate').after(A_DATE).returnAll() - - expect(entities).toHaveLength(1) - expect(entities).toEqual([ { [EntityKeyName]: 'search-hash:3', ...A_THIRD_ENTITY } ]) - }) - - it("searches a array", async () => { - entities = await repository.search().where('someStrings').contains('charlie').returnAll() - - expect(entities).toHaveLength(3) - expect(entities).toEqual(expect.arrayContaining([ - { [EntityKeyName]: 'search-hash:1', ...AN_ENTITY }, - { [EntityKeyName]: 'search-hash:2', ...ANOTHER_ENTITY }, - { [EntityKeyName]: 'search-hash:3', ...A_THIRD_ENTITY } - ])) - }) - - it("searches all the field types", async () => { - entities = await repository.search() - .where('aString').eq('foo') - .and('someText').matches('dog') - .and('aNumber').gte(42) - .and('aBoolean').true() - .and('aPoint').inCircle(circle => circle.origin(A_POINT).radius(10).meters) - .and('someStrings').contains('alfa') - .returnAll() - - expect(entities).toHaveLength(1) - expect(entities).toEqual([ { [EntityKeyName]: 'search-hash:1', ...AN_ENTITY } ]) - }) - - it("searches a string with escaped punctuation", async () => { - entities = await repository.search().where('aString').equals('foo ,.<>{}[]"\':;!@#$%^()-+=~& bar').returnAll() - - expect(entities).toHaveLength(1) - expect(entities).toEqual([ { [EntityKeyName]: 'search-hash:escaped', ...AN_ESCAPED_ENTITY } ]) - }) - - it("searches a text with escaped punctuation", async () => { - entities = await repository.search().where('someText').matches('zany').returnAll() - - expect(entities).toHaveLength(1) - expect(entities).toEqual([ { [EntityKeyName]: 'search-hash:escaped', ...AN_ESCAPED_ENTITY } ]) - }) - - it("searches an array with escaped punctuation", async () => { - entities = await repository.search().where('someStrings').contains('alfa ,.<>{}[]"\':;!@#$%^&()-+=~ bravo').returnAll() - - expect(entities).toHaveLength(1) - expect(entities).toEqual([ { [EntityKeyName]: 'search-hash:escaped', ...AN_ESCAPED_ENTITY } ]) - }) -}) diff --git a/spec/functional/search/search-json.spec.ts b/spec/functional/search/search-json.spec.ts deleted file mode 100644 index c35e9d2..0000000 --- a/spec/functional/search/search-json.spec.ts +++ /dev/null @@ -1,206 +0,0 @@ -import '../../helpers/custom-matchers' - -import { createClient } from 'redis' - -import { Entity, EntityKeyName, RedisConnection, Repository, Schema, SearchError } from '$lib/index' - -import { createJsonEntitySchema } from '../helpers/data-helper' -import { removeKeys, saveJson, sleep } from '../helpers/redis-helper' - -import { A_POINT, A_DATE } from '../../helpers/example-data' -import { ANOTHER_ENTITY, ANOTHER_JSON, AN_ENTITY, AN_ESCAPED_ENTITY, AN_ESCAPED_JSON, A_JSON, A_THIRD_ENTITY, A_THIRD_JSON } from '../helpers/json-example-data' - -describe("search for JSON documents", () => { - - let redis: RedisConnection - let repository: Repository - let schema: Schema - let entities: Entity[] - - beforeAll(async () => { - redis = createClient() - await redis.connect() - - await removeKeys(redis, 'search-json:1', 'search-json:2', 'search-json:3', 'search-json:escaped') - await saveJson(redis, 'search-json:1', A_JSON) - await saveJson(redis, 'search-json:2', ANOTHER_JSON) - await saveJson(redis, 'search-json:3', A_THIRD_JSON) - await saveJson(redis, 'search-json:escaped', AN_ESCAPED_JSON) - - schema = createJsonEntitySchema('search-json') - repository = new Repository(schema, redis) - - await repository.createIndex() - await sleep(50) // Yuck! Gotta wait for RediSearch to index everything. - }) - - afterAll(async () => { - await removeKeys(redis, 'search-json:1', 'search-json:2', 'search-json:3', 'search-json:escaped') - await repository.dropIndex() - await redis.quit() - }) - - it("performs a wildcard search", async () => { - entities = await repository.search().returnAll() - - expect(entities).toHaveLength(4) - expect(entities).toEqual(expect.arrayContaining([ - { [EntityKeyName]: 'search-json:1', ...AN_ENTITY }, - { [EntityKeyName]: 'search-json:2', ...ANOTHER_ENTITY }, - { [EntityKeyName]: 'search-json:3', ...A_THIRD_ENTITY }, - { [EntityKeyName]: 'search-json:escaped', ...AN_ESCAPED_ENTITY } - ])) - }) - - it("performs a sorted search", async () => { - entities = await repository.search().sortAscending('aNumber').returnAll() - - expect(entities).toHaveLength(4) - expect(entities).toEqual([ - { [EntityKeyName]: 'search-json:3', ...A_THIRD_ENTITY }, - { [EntityKeyName]: 'search-json:2', ...ANOTHER_ENTITY }, - { [EntityKeyName]: 'search-json:1', ...AN_ENTITY }, - { [EntityKeyName]: 'search-json:escaped', ...AN_ESCAPED_ENTITY } - ]) - }) - - it("performs a paginated search", async () => { - entities = await repository.search().returnAll({ pageSize: 2 }) - - expect(entities).toHaveLength(4) - expect(entities).toEqual(expect.arrayContaining([ - { [EntityKeyName]: 'search-json:1', ...AN_ENTITY }, - { [EntityKeyName]: 'search-json:2', ...ANOTHER_ENTITY }, - { [EntityKeyName]: 'search-json:3', ...A_THIRD_ENTITY }, - { [EntityKeyName]: 'search-json:escaped', ...AN_ESCAPED_ENTITY } - ])) - }) - - it("performs a raw search", async () => { - entities = await repository.searchRaw('@aString:{foo} @aNumber:[42 42]').returnAll() - - expect(entities).toHaveLength(1) - expect(entities).toEqual([ { [EntityKeyName]: 'search-json:1', ...AN_ENTITY } ]) - }) - - it("searches a string", async () => { - entities = await repository.search().where('aString').eq('foo').returnAll() - - expect(entities).toHaveLength(1) - expect(entities).toEqual([ { [EntityKeyName]: 'search-json:1', ...AN_ENTITY } ]) - }) - - it("searches a text", async () => { - entities = await repository.search().where('someText').matches('brown quick').returnAll() - - expect(entities).toHaveLength(1) - expect(entities).toEqual([ { [EntityKeyName]: 'search-json:1', ...AN_ENTITY } ]) - }) - - it("searches a text with an exact match", async () => { - entities = await repository.search().where('someText').exactly.matches('quick brown').returnAll() - - expect(entities).toHaveLength(1) - expect(entities).toEqual([ { [EntityKeyName]: 'search-json:1', ...AN_ENTITY } ]) - }) - - it("searches a text with stop words", async () => { - entities = await repository.search().where('someText').matches('brown quick the').returnAll() - - expect(entities).toHaveLength(1) - expect(entities).toEqual([ { [EntityKeyName]: 'search-json:1', ...AN_ENTITY } ]) - }) - - it("throws an error when searching a text with an exact match and stop words", async () => { - expect.assertions(2) - try { - await repository.search().where('someText').exactly.matches('the quick brown').returnAll() - } catch (error) { - const err = error as Error - expect(err).toBeInstanceOf(SearchError) - expect(err.message).toEqual(`The query to RediSearch had a syntax error: "Syntax error at offset 12 near the".\nThis is often the result of using a stop word in the query. Either change the query to not use a stop word or change the stop words in the schema definition. You can check the RediSearch source for the default stop words at: https://github.com/RediSearch/RediSearch/blob/master/src/stopwords.h.`) - } - }) - - it("searches a number", async () => { - entities = await repository.search().where('aNumber').lte(23).returnAll() - - expect(entities).toHaveLength(2) - expect(entities).toEqual(expect.arrayContaining([ - { [EntityKeyName]: 'search-json:2', ...ANOTHER_ENTITY }, - { [EntityKeyName]: 'search-json:3', ...A_THIRD_ENTITY } - ])) - }) - - it("searches a boolean", async () => { - entities = await repository.search().where('aBoolean').true().returnAll() - - expect(entities).toHaveLength(2) - expect(entities).toEqual(expect.arrayContaining([ - { [EntityKeyName]: 'search-json:1', ...AN_ENTITY }, - { [EntityKeyName]: 'search-json:2', ...ANOTHER_ENTITY } - ])) - }) - - it("searches a point", async () => { - entities = await repository.search() - .where('aPoint').inCircle(circle => circle.origin(A_POINT).radius(10).meters) - .returnAll() - - expect(entities).toHaveLength(1) - expect(entities).toEqual([ { [EntityKeyName]: 'search-json:1', ...AN_ENTITY } ]) - }) - - it("searches a date", async () => { - entities = await repository.search().where('aDate').after(A_DATE).returnAll() - - expect(entities).toHaveLength(1) - expect(entities).toEqual([ { [EntityKeyName]: 'search-json:3', ...A_THIRD_ENTITY } ]) - }) - - it("searches a array", async () => { - entities = await repository.search().where('someStrings').contains('charlie').returnAll() - - expect(entities).toHaveLength(3) - expect(entities).toEqual(expect.arrayContaining([ - { [EntityKeyName]: 'search-json:1', ...AN_ENTITY }, - { [EntityKeyName]: 'search-json:2', ...ANOTHER_ENTITY }, - { [EntityKeyName]: 'search-json:3', ...A_THIRD_ENTITY } - ])) - }) - - it("searches all the field types", async () => { - entities = await repository.search() - .where('aString').eq('foo') - .and('someText').matches('dog') - .and('aNumber').gte(42) - .and('aBoolean').true() - .and('aPoint').inCircle(circle => circle.origin(12.34, 56.78).radius(10).meters) - .and('someStrings').contains('alfa') - .returnAll() - - expect(entities).toHaveLength(1) - expect(entities).toEqual([ { [EntityKeyName]: 'search-json:1', ...AN_ENTITY } ]) - }) - - it("searches a string with escaped punctuation", async () => { - entities = await repository.search().where('aString').equals('foo ,.<>{}[]"\':;!@#$%^()-+=~& bar').returnAll() - - expect(entities).toHaveLength(1) - expect(entities).toEqual([ { [EntityKeyName]: 'search-json:escaped', ...AN_ESCAPED_ENTITY } ]) - }) - - it("searches a text with escaped punctuation", async () => { - entities = await repository.search().where('someText').matches('zany').returnAll() - - expect(entities).toHaveLength(1) - expect(entities).toEqual([ { [EntityKeyName]: 'search-json:escaped', ...AN_ESCAPED_ENTITY } ]) - }) - - it("searches a array with escaped punctuation", async () => { - entities = await repository.search().where('someStrings').contains('alfa ,.<>{}[]"\':;!@#$%^&()-+=~ bravo').returnAll() - - expect(entities).toHaveLength(1) - expect(entities).toEqual([ { [EntityKeyName]: 'search-json:escaped', ...AN_ESCAPED_ENTITY } ]) - }) -}) diff --git a/spec/helpers/custom-matchers.ts b/spec/helpers/custom-matchers.ts deleted file mode 100644 index a1d78be..0000000 --- a/spec/helpers/custom-matchers.ts +++ /dev/null @@ -1,38 +0,0 @@ -export {} - -declare global { - namespace jest { - interface AsymmetricMatchers { - toThrowErrorOfType(expectedType: any, expectedMessage: string): void - } - interface Matchers { - toThrowErrorOfType(expectedType: any, expectedMessage: string): R - } - } -} - -expect.extend({ - toThrowErrorOfType(fnOrError, expectedType, expectedMessage) { - - let pass: boolean, actual: any - - if (fnOrError instanceof Function) { - try { - fnOrError() - } catch (error: any) { - actual = error - } - } else { - actual = fnOrError - } - - pass = actual.constructor.name === expectedType.name && actual.message === expectedMessage - - return { - pass, - message: () => - `Expected${ pass ? ' not ' : ' ' }to throw ${expectedType.name} with message "${expectedMessage}".\n` + - `Received: ${actual.constructor.name} with message "${actual.message}"` - } - } -}) diff --git a/spec/helpers/example-data.ts b/spec/helpers/example-data.ts deleted file mode 100644 index 41a518e..0000000 --- a/spec/helpers/example-data.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { Point } from "$lib/entity" - -export const A_STRING = 'foo' -export const ANOTHER_STRING = 'bar' -export const A_THIRD_STRING = 'baz' - -export const SOME_TEXT = "The quick brown fox jumped over the lazy dog." -export const SOME_OTHER_TEXT = "The five boxing wizards jump quickly." -export const SOME_MORE_TEXT = "How vexingly quick daft zebras jump!" - -export const A_NUMBER = 42 -export const A_NUMBER_STRING = A_NUMBER.toString() - -export const ANOTHER_NUMBER = 23 -export const ANOTHER_NUMBER_STRING = ANOTHER_NUMBER.toString() - -export const A_THIRD_NUMBER = 13 -export const A_THIRD_NUMBER_STRING = A_THIRD_NUMBER.toString() - -export const A_DATE: Date = new Date('1997-07-04T16:56:55.456Z') -export const A_DATE_ISO: string = A_DATE.toISOString() -export const A_DATE_EPOCH: number = A_DATE.getTime() / 1000 -export const A_DATE_EPOCH_STRING: string = A_DATE_EPOCH.toString() - -export const ANOTHER_DATE: Date = new Date('1969-07-20T20:17:40.000Z') -export const ANOTHER_DATE_ISO: string = ANOTHER_DATE.toISOString() -export const ANOTHER_DATE_EPOCH: number = ANOTHER_DATE.getTime() / 1000 -export const ANOTHER_DATE_EPOCH_STRING: string = ANOTHER_DATE_EPOCH.toString() - -export const A_THIRD_DATE: Date = new Date('2015-07-14T11:49:57.000Z') -export const A_THIRD_DATE_ISO: string = A_THIRD_DATE.toISOString() -export const A_THIRD_DATE_EPOCH: number = A_THIRD_DATE.getTime() / 1000 -export const A_THIRD_DATE_EPOCH_STRING: string = A_THIRD_DATE_EPOCH.toString() - -export const A_POINT: Point = { longitude: 12.34, latitude: 56.78 } -export const A_POINT_JSON: string = JSON.stringify(A_POINT) -export const A_POINT_STRING: string = `${A_POINT.longitude},${A_POINT.latitude}` -export const A_POINT_PRETTY_JSON: string = JSON.stringify(A_POINT, null, 1) - -export const ANOTHER_POINT: Point = { longitude: -23.45, latitude: 67.89 } -export const ANOTHER_POINT_JSON: string = JSON.stringify(ANOTHER_POINT) -export const ANOTHER_POINT_STRING: string = `${ANOTHER_POINT.longitude},${ANOTHER_POINT.latitude}` - -export const A_THIRD_POINT: Point = { longitude: -34.56, latitude: 78.90 } -export const A_THIRD_POINT_JSON: string = JSON.stringify(A_THIRD_POINT) -export const A_THIRD_POINT_STRING: string = `${A_THIRD_POINT.longitude},${A_THIRD_POINT.latitude}` - -export const AN_INVALID_POINT: Point = { longitude: 123.4, latitude: 85.05112879 } -export const AN_INVALID_POINT_STRING: string = `${AN_INVALID_POINT.longitude},${AN_INVALID_POINT.latitude}` - -export const A_PARITAL_POINT = { latitude: 85.05112879 } - -export const SOME_STRINGS: Array = ['alfa', 'bravo', 'charlie'] -export const SOME_STRINGS_JSON: string = JSON.stringify(SOME_STRINGS) -export const SOME_STRINGS_JOINED: string = SOME_STRINGS.join('|') - -export const SOME_OTHER_STRINGS: Array = ['bravo', 'charlie', 'delta'] -export const SOME_OTHER_STRINGS_JSON: string = JSON.stringify(SOME_OTHER_STRINGS) -export const SOME_OTHER_STRINGS_JOINED: string = SOME_OTHER_STRINGS.join('|') - -export const SOME_MORE_STRINGS: Array = ['charlie', 'delta', 'echo'] -export const SOME_MORE_STRINGS_JSON: string = JSON.stringify(SOME_MORE_STRINGS) -export const SOME_MORE_STRINGS_JOINED: string = SOME_MORE_STRINGS.join('|') diff --git a/spec/schema/array.spec.ts b/spec/schema/array.spec.ts new file mode 100644 index 0000000..8b76c8b --- /dev/null +++ b/spec/schema/array.spec.ts @@ -0,0 +1,28 @@ +import { schemaData } from "../../src/utils"; +import { arraySchema } from "../constants"; + +describe("array fields", () => { + test("raw data", () => { + expect(arraySchema.rawData).toStrictEqual({ + arrayField1: "array", + arrayField2: { type: "array" }, + arrayField3: { type: "array", elements: "number" }, + arrayField4: { type: "array", default: ["s"] }, + arrayField5: { type: "array", required: true }, + arrayField6: { type: "array", elements: "boolean", default: [false], required: true }, + arrayField7: { type: "array", elements: { element1: "string" } } + }); + }); + + test("formatted data", () => { + expect(arraySchema[schemaData]).toStrictEqual({ + arrayField1: { type: "array", elements: "string", default: undefined, required: false }, + arrayField2: { type: "array", elements: "string", default: undefined, required: false }, + arrayField3: { type: "array", elements: "number", default: undefined, required: false }, + arrayField4: { type: "array", elements: "string", default: ["s"], required: false }, + arrayField5: { type: "array", elements: "string", default: undefined, required: true }, + arrayField6: { type: "array", elements: "boolean", default: [false], required: true }, + arrayField7: { type: "array", elements: { element1: { type: "string", default: undefined, required: false } }, default: undefined, required: false } + }); + }); +}) \ No newline at end of file diff --git a/spec/schema/boolean.spec.ts b/spec/schema/boolean.spec.ts new file mode 100644 index 0000000..de7971c --- /dev/null +++ b/spec/schema/boolean.spec.ts @@ -0,0 +1,24 @@ +import { schemaData } from "../../src/utils"; +import { booleanSchema } from "../constants"; + +describe("boolean fields", () => { + test("raw data", () => { + expect(booleanSchema.rawData).toStrictEqual({ + booleanField1: "boolean", + booleanField2: { type: "boolean" }, + booleanField3: { type: "boolean", default: true }, + booleanField4: { type: "boolean", required: true }, + booleanField5: { type: "boolean", default: false, required: true } + }); + }); + + test("formatted data", () => { + expect(booleanSchema[schemaData]).toStrictEqual({ + booleanField1: { type: "boolean", default: undefined, required: false }, + booleanField2: { type: "boolean", default: undefined, required: false }, + booleanField3: { type: "boolean", default: true, required: false }, + booleanField4: { type: "boolean", default: undefined, required: true }, + booleanField5: { type: "boolean", default: false, required: true } + }); + }); +}) \ No newline at end of file diff --git a/spec/schema/date.spec.ts b/spec/schema/date.spec.ts new file mode 100644 index 0000000..740476b --- /dev/null +++ b/spec/schema/date.spec.ts @@ -0,0 +1,24 @@ +import { schemaData } from "../../src/utils"; +import { dateSchema } from "../constants"; + +describe("date fields", () => { + test("raw data", () => { + expect(dateSchema.rawData).toStrictEqual({ + dateField1: "date", + dateField2: { type: "date" }, + dateField3: { type: "date", default: 874195200000 }, + dateField4: { type: "date", required: true }, + dateField5: { type: "date", default: new Date(874195200000), required: true } + }); + }); + + test("formatted data", () => { + expect(dateSchema[schemaData]).toStrictEqual({ + dateField1: { type: "date", default: undefined, required: false }, + dateField2: { type: "date", default: undefined, required: false }, + dateField3: { type: "date", default: 874195200000, required: false }, + dateField4: { type: "date", default: undefined, required: true }, + dateField5: { type: "date", default: 874195200000, required: true } + }); + }); +}) \ No newline at end of file diff --git a/spec/schema/number.spec.ts b/spec/schema/number.spec.ts new file mode 100644 index 0000000..45edf28 --- /dev/null +++ b/spec/schema/number.spec.ts @@ -0,0 +1,24 @@ +import { schemaData } from "../../src/utils"; +import { numberSchema } from "../constants"; + +describe("number fields", () => { + test("raw data", () => { + expect(numberSchema.rawData).toStrictEqual({ + numberField1: "number", + numberField2: { type: "number" }, + numberField3: { type: "number", default: 3 }, + numberField4: { type: "number", required: true }, + numberField5: { type: "number", default: 5, required: true } + }); + }); + + test("formatted data", () => { + expect(numberSchema[schemaData]).toStrictEqual({ + numberField1: { type: "number", default: undefined, required: false }, + numberField2: { type: "number", default: undefined, required: false }, + numberField3: { type: "number", default: 3, required: false }, + numberField4: { type: "number", default: undefined, required: true }, + numberField5: { type: "number", default: 5, required: true } + }); + }); +}) \ No newline at end of file diff --git a/spec/schema/object.spec.ts b/spec/schema/object.spec.ts new file mode 100644 index 0000000..8d8a7d5 --- /dev/null +++ b/spec/schema/object.spec.ts @@ -0,0 +1,90 @@ +import { schemaData } from "../../src/utils"; +import { objectSchema } from "../constants"; + +describe("object field", () => { + test("raw data", () => { + expect(objectSchema.rawData).toStrictEqual({ + objectField1: { + type: "object", + properties: { + nestedProperty1: "string", + nestedProperty2: "number", + nestedProperty3: "boolean", + nestedProperty4: "text", + nestedProperty5: "date", + nestedProperty6: "point", + nestedProperty7: "array", + nestedProperty9: { + type: "object", + properties: { + deep1: "string", + deep2: { + type: "object", + properties: { + nest: { + type: "object", + properties: { + finalNestBczImLazy: "array" + } + } + } + } + } + } + }, + default: { + nestedProperty9: { + deep1: "S" + } + }, + required: true + } + }); + }); + + test("formatted data", () => { + expect(objectSchema[schemaData]).toStrictEqual({ + objectField1: { + type: "object", + properties: { + nestedProperty1: { type: "string", default: undefined, required: false }, + nestedProperty2: { type: "number", default: undefined, required: false }, + nestedProperty3: { type: "boolean", default: undefined, required: false }, + nestedProperty4: { type: "text", default: undefined, required: false }, + nestedProperty5: { type: "date", default: undefined, required: false }, + nestedProperty6: { type: "point", default: undefined, required: false }, + nestedProperty7: { type: "array", elements: "string", default: undefined, required: false }, + nestedProperty9: { + type: "object", + properties: { + deep1: { type: "string", default: undefined, required: false }, + deep2: { + type: "object", + properties: { + nest: { + type: "object", + properties: { + finalNestBczImLazy: { type: "array", elements: "string", default: undefined, required: false } + }, + default: undefined, + required: false + } + }, + default: undefined, + required: false + } + }, + default: undefined, + required: false + } + }, + default: { + nestedProperty9: { + deep1: "S" + } + }, + required: true + } + }) + }); +}) \ No newline at end of file diff --git a/spec/schema/parser.spec.ts b/spec/schema/parser.spec.ts new file mode 100644 index 0000000..ebe5672 --- /dev/null +++ b/spec/schema/parser.spec.ts @@ -0,0 +1,10 @@ +import { Schema } from "../../src" + +describe("Test if parser is throwing errors when fields are passed in wrong", () => { + test("Object as string", () => { + expect(() => new Schema({ + //@ts-expect-error This is on purpose to see if the parser is throwing or not + objectField: "object" + })).toThrow(); + }); +}) \ No newline at end of file diff --git a/spec/schema/point.spec.ts b/spec/schema/point.spec.ts new file mode 100644 index 0000000..13326b5 --- /dev/null +++ b/spec/schema/point.spec.ts @@ -0,0 +1,24 @@ +import { schemaData } from "../../src/utils"; +import { pointSchema } from "../constants"; + +describe("point fields", () => { + test("raw data", () => { + expect(pointSchema.rawData).toStrictEqual({ + pointField1: "point", + pointField2: { type: "point" }, + pointField3: { type: "point", default: { longitude: 3, latitude: 3 } }, + pointField4: { type: "point", required: true }, + pointField5: { type: "point", default: { longitude: 5, latitude: 5 }, required: true } + }); + }); + + test("formatted data", () => { + expect(pointSchema[schemaData]).toStrictEqual({ + pointField1: { type: "point", default: undefined, required: false }, + pointField2: { type: "point", default: undefined, required: false }, + pointField3: { type: "point", default: { longitude: 3, latitude: 3 }, required: false }, + pointField4: { type: "point", default: undefined, required: true }, + pointField5: { type: "point", default: { longitude: 5, latitude: 5 }, required: true } + }); + }); +}) \ No newline at end of file diff --git a/spec/schema/string.spec.ts b/spec/schema/string.spec.ts new file mode 100644 index 0000000..23493b9 --- /dev/null +++ b/spec/schema/string.spec.ts @@ -0,0 +1,24 @@ +import { schemaData } from "../../src/utils"; +import { stringSchema } from "../constants"; + +describe("string fields", () => { + test("raw data", () => { + expect(stringSchema.rawData).toStrictEqual({ + stringField1: "string", + stringField2: { type: "string" }, + stringField3: { type: "string", default: "S" }, + stringField4: { type: "string", required: true }, + stringField5: { type: "string", default: "SS", required: true } + }); + }); + + test("formatted data", () => { + expect(stringSchema[schemaData]).toStrictEqual({ + stringField1: { type: "string", default: undefined, required: false }, + stringField2: { type: "string", default: undefined, required: false }, + stringField3: { type: "string", default: "S", required: false }, + stringField4: { type: "string", default: undefined, required: true }, + stringField5: { type: "string", default: "SS", required: true } + }); + }); +}) \ No newline at end of file diff --git a/spec/schema/text.spec.ts b/spec/schema/text.spec.ts new file mode 100644 index 0000000..fa91ec5 --- /dev/null +++ b/spec/schema/text.spec.ts @@ -0,0 +1,24 @@ +import { schemaData } from "../../src/utils"; +import { textSchema } from "../constants"; + +describe("text fields", () => { + test("raw data", () => { + expect(textSchema.rawData).toStrictEqual({ + textField1: "text", + textField2: { type: "text" }, + textField3: { type: "text", default: "T" }, + textField4: { type: "text", required: true }, + textField5: { type: "text", default: "TT", required: true } + }); + }); + + test("formatted data", () => { + expect(textSchema[schemaData]).toStrictEqual({ + textField1: { type: "text", default: undefined, required: false }, + textField2: { type: "text", default: undefined, required: false }, + textField3: { type: "text", default: "T", required: false }, + textField4: { type: "text", default: undefined, required: true }, + textField5: { type: "text", default: "TT", required: true } + }); + }); +}) \ No newline at end of file diff --git a/spec/tsconfig.json b/spec/tsconfig.json new file mode 100644 index 0000000..b5e7ce9 --- /dev/null +++ b/spec/tsconfig.json @@ -0,0 +1,8 @@ +// This file is just for intellisense purposes +{ + "compilerOptions": { + "types": [ + "vitest/globals" + ] + } +} \ No newline at end of file diff --git a/spec/unit/client/client-close.spec.ts b/spec/unit/client/client-close.spec.ts deleted file mode 100644 index 700e94c..0000000 --- a/spec/unit/client/client-close.spec.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { redis } from '../helpers/mock-redis' - -import { Client } from '$lib/client' - -describe("Client", () => { - - let client: Client - - beforeEach(() => { client = new Client() }) - - describe("#close", () => { - describe("when called on an open client", () => { - beforeEach(async () => { - await client.open() - await client.close() - }) - - it("closes the Redis connection", async () => expect(redis.quit).toHaveBeenCalled()) - it("is no longer open", async () => expect(client.isOpen()).toBe(false)) - }) - - describe("when called on an already closed client", () => { - beforeEach(async () => { - await client.open() - await client.close() - }) - - it("happily closes it anyways", async () => { - await client.close() - expect(client.isOpen()).toBe(false) - }) - }) - - it("happily closes an unopened client", async () => { - await client.close() - expect(client.isOpen()).toBe(false) - }) - }) -}) diff --git a/spec/unit/client/client-create-index.spec.ts b/spec/unit/client/client-create-index.spec.ts deleted file mode 100644 index 6a0a190..0000000 --- a/spec/unit/client/client-create-index.spec.ts +++ /dev/null @@ -1,55 +0,0 @@ -import '../../helpers/custom-matchers' - -import { RediSearchSchema, SchemaFieldTypes } from 'redis' - -import { redis } from '../helpers/mock-redis' -import { Client, CreateOptions } from '$lib/client' -import { RedisOmError } from '$lib/error' - - - -const schema: RediSearchSchema = { - foo: { type: SchemaFieldTypes.TAG }, - bar: { type: SchemaFieldTypes.TEXT } -} - -const options: CreateOptions = { - ON: 'HASH', - PREFIX: 'prefix', - STOPWORDS: ['bar', 'baz', 'qux'] -} - -describe("Client", () => { - - let client: Client - - beforeEach(() => { client = new Client() }) - - describe("#createIndex", () => { - describe("when called on an open client", () => { - beforeEach(async () => { - await client.open() - }) - - it("passes a command to redis", async () => { - await client.createIndex('index', schema, options) - expect(redis.ft.create).toHaveBeenCalledWith('index', schema, options) - }) - }) - - describe("when called on a closed client", () => { - beforeEach(async () => { - await client.open() - await client.close() - }) - - it("errors when called on a closed client", () => - expect(async () => await client.createIndex('index', schema, options)) - .rejects.toThrowErrorOfType(RedisOmError, "Redis connection needs to be open.")) - }) - - it("errors when called on a new client", async () => - expect(async () => await client.createIndex('index', schema, options)) - .rejects.toThrowErrorOfType(RedisOmError, "Redis connection needs to be open.")) - }) -}) diff --git a/spec/unit/client/client-drop-index.spec.ts b/spec/unit/client/client-drop-index.spec.ts deleted file mode 100644 index a591a60..0000000 --- a/spec/unit/client/client-drop-index.spec.ts +++ /dev/null @@ -1,42 +0,0 @@ -import '../../helpers/custom-matchers' - -import { ft } from '../helpers/mock-redis' - -import { Client } from '$lib/client' -import { RedisOmError } from '$lib/error' - - -describe("Client", () => { - - let client: Client - - beforeEach(() => { client = new Client() }) - - describe("#dropIndex", () => { - describe("when called on an open client", () => { - beforeEach(async () => { - await client.open() - }) - - it("passes the command to redis", async () => { - await client.dropIndex('index') - expect(ft.dropIndex).toHaveBeenCalledWith('index') - }) - }) - - describe("when called on a closed client", () => { - beforeEach(async () => { - await client.open() - await client.close() - }) - - it("errors when called on a closed client", () => - expect(async () => await client.dropIndex('index')) - .rejects.toThrowErrorOfType(RedisOmError, "Redis connection needs to be open.")) - }) - - it("errors when called on a new client", async () => - expect(async () => await client.dropIndex('index')) - .rejects.toThrowErrorOfType(RedisOmError, "Redis connection needs to be open.")) - }) -}) diff --git a/spec/unit/client/client-expire.spec.ts b/spec/unit/client/client-expire.spec.ts deleted file mode 100644 index a98adcf..0000000 --- a/spec/unit/client/client-expire.spec.ts +++ /dev/null @@ -1,41 +0,0 @@ -import '../../helpers/custom-matchers' - -import { redis } from '../helpers/mock-redis' - -import { Client } from '$lib/client' -import { RedisOmError } from '$lib/error' - -describe("Client", () => { - - let client: Client - - beforeEach(() => { client = new Client() }) - - describe("#expire", () => { - describe("when called on an open client", () => { - beforeEach(async () => { - await client.open() - }) - - it("passes the command to redis", async () => { - await client.expire('foo', 60) - expect(redis.expire).toHaveBeenCalledWith('foo', 60) - }) - }) - - describe("when called on a closed client", () => { - beforeEach(async () => { - await client.open() - await client.close() - }) - - it("errors when called on a closed client", () => - expect(async () => await client.expire('foo', 60)) - .rejects.toThrowErrorOfType(RedisOmError, "Redis connection needs to be open.")) - }) - - it("errors when called on a new client", async () => - expect(async () => await client.expire('foo', 60)) - .rejects.toThrowErrorOfType(RedisOmError, "Redis connection needs to be open.")) - }) -}) diff --git a/spec/unit/client/client-fetch-repository.spec.ts b/spec/unit/client/client-fetch-repository.spec.ts deleted file mode 100644 index 985599c..0000000 --- a/spec/unit/client/client-fetch-repository.spec.ts +++ /dev/null @@ -1,61 +0,0 @@ -import '../../helpers/custom-matchers' - -import { Client } from '$lib/client' -import { RedisOmError } from '$lib/error' -import { Repository } from '$lib/repository' -import { Schema } from '$lib/schema/schema' - -vi.mock('$lib/repository') - -describe("Client", () => { - - let client: Client - - beforeEach(() => { client = new Client() }) - afterEach(() => { client.close() }) - - it("passes", () => expect(true).toBe(true)) - - describe("#fetchRepository", () => { - - let repository: Repository - let schema: Schema - - describe("when fetching a Repository", () => { - beforeAll(() => { - schema = new Schema("TestEntity", {}) - }) - - describe("when called on an open client", () => { - - beforeEach(async () => { - await client.open() - repository = client.fetchRepository(schema) - }) - - it("creates a repository with the schema and client", () => { - expect(Repository).toHaveBeenCalledWith(schema, client) - }) - - it("returns a repository", async () => { - expect(repository).toBeInstanceOf(Repository) - }) - }) - - describe("when called on a closed client", () => { - beforeEach(async () => { - await client.open() - await client.close() - }) - - it("errors when called on a closed client", () => - expect(() => client.fetchRepository(schema)) - .toThrowErrorOfType(RedisOmError, "Redis connection needs to be open.")) - }) - - it("errors when called on a new client", () => - expect(() => client.fetchRepository(schema)) - .toThrowErrorOfType(RedisOmError, "Redis connection needs to be open.")) - }) - }) -}) diff --git a/spec/unit/client/client-get-set.spec.ts b/spec/unit/client/client-get-set.spec.ts deleted file mode 100644 index 6d027a0..0000000 --- a/spec/unit/client/client-get-set.spec.ts +++ /dev/null @@ -1,92 +0,0 @@ -import '../../helpers/custom-matchers' - -import { redis } from '../helpers/mock-redis' - -import { Client } from '$lib/client' -import { RedisOmError } from '$lib/error' - -describe("Client", () => { - - let client: Client - let result: string | null - - beforeEach(() => { client = new Client() }) - - describe("#get", () => { - describe("when called on an open client", () => { - beforeEach(async () => { - await client.open() - }) - - describe("and the result is a string", () => { - beforeEach(async () => { - redis.get.mockReturnValue('bar') - result = await client.get('foo') - }) - - it("passes the command to redis", async () => { - expect(redis.get).toHaveBeenCalledWith('foo') - }) - - it("returns the result", async () => expect(result).toBe('bar')) - }) - - describe("and the result is null", () => { - beforeEach(async () => { - redis.get.mockResolvedValue(null) - result = await client.get('foo') - }) - - it("passes the command to redis", async () => { - expect(redis.get).toHaveBeenCalledWith('foo') - }) - - it("returns the result", async () => expect(result).toBeNull()) - }) - }) - - describe("when called on a closed client", () => { - beforeEach(async () => { - await client.open() - await client.close() - }) - - it("errors when called on a closed client", () => - expect(async () => await client.get('foo')) - .rejects.toThrowErrorOfType(RedisOmError, "Redis connection needs to be open.")) - }) - - it("errors when called on a new client", async () => - expect(async () => await client.get('foo')) - .rejects.toThrowErrorOfType(RedisOmError, "Redis connection needs to be open.")) - }) - - describe("#set", () => { - describe("when called on an open client", () => { - beforeEach(async () => { - await client.open() - await client.set('foo', 'bar') - }) - - it("passes the command to redis", async () => { - expect(redis.set).toHaveBeenCalledWith('foo', 'bar') - }) - }) - - describe("when called on a closed client", () => { - beforeEach(async () => { - await client.open() - await client.close() - }) - - it("errors when called on a closed client", () => - expect(async () => await client.set('foo', 'bar')) - .rejects.toThrowErrorOfType(RedisOmError, "Redis connection needs to be open.")) - }) - - it("errors when called on a new client", async () => - expect(async () => await client.set('foo', 'bar')) - .rejects.toThrowErrorOfType(RedisOmError, "Redis connection needs to be open.")) - }) - -}) diff --git a/spec/unit/client/client-hgetall.spec.ts b/spec/unit/client/client-hgetall.spec.ts deleted file mode 100644 index 115d874..0000000 --- a/spec/unit/client/client-hgetall.spec.ts +++ /dev/null @@ -1,47 +0,0 @@ -import '../../helpers/custom-matchers' - -import { redis } from '../helpers/mock-redis' - -import { Client } from '$lib/client' -import { RedisOmError } from '$lib/error' - -describe("Client", () => { - - let client: Client - let result: { [key: string]: string } - - beforeEach(() => { client = new Client() }) - - describe("#hgetall", () => { - describe("when called on an open client", () => { - beforeEach(async () => { - await client.open() - redis.hGetAll.mockResolvedValue({ foo: 'bar', baz: 'qux' }) - result = await client.hgetall('foo') - }) - - it("passes the command to redis", async () => { - expect(redis.hGetAll).toHaveBeenCalledWith('foo') - }) - - it("returns the value from redis", async () => { - expect(result).toEqual({ foo: 'bar', baz: 'qux' }) - }) - }) - - describe("when called on a closed client", () => { - beforeEach(async () => { - await client.open() - await client.close() - }) - - it("errors when called on a closed client", () => - expect(async () => await client.hgetall('foo')) - .rejects.toThrowErrorOfType(RedisOmError, "Redis connection needs to be open.")) - }) - - it("errors when called on a new client", async () => - expect(async () => await client.hgetall('foo')) - .rejects.toThrowErrorOfType(RedisOmError, "Redis connection needs to be open.")) - }) -}) diff --git a/spec/unit/client/client-hsetall.spec.ts b/spec/unit/client/client-hsetall.spec.ts deleted file mode 100644 index 673ce38..0000000 --- a/spec/unit/client/client-hsetall.spec.ts +++ /dev/null @@ -1,45 +0,0 @@ -import '../../helpers/custom-matchers' - -import { redis, multi } from '../helpers/mock-redis' - -import { Client } from '$lib/client' -import { RedisOmError } from '$lib/error' - -describe("Client", () => { - - let client: Client - - beforeEach(() => { client = new Client() }) - - describe("#hsetall", () => { - describe("when called on an open client", () => { - beforeEach(async () => { - await client.open() - }) - - it("passes the command to redis", async () => { - // it's a bit of an ugly mirror but will do - await client.hsetall('foo', { foo: 'bar', baz: 'qux' }) - expect(redis.multi).toHaveBeenCalled() - expect(multi.unlink).toHaveBeenCalledWith('foo') - expect(multi.hSet).toHaveBeenCalledWith('foo', { foo: 'bar', baz: 'qux' }) - expect(multi.exec).toHaveBeenCalled() - }) - }) - - describe("when called on a closed client", () => { - beforeEach(async () => { - await client.open() - await client.close() - }) - - it("errors when called on a closed client", () => - expect(async () => await client.hsetall('foo', { foo: 'bar', baz: 'qux' })) - .rejects.toThrowErrorOfType(RedisOmError, "Redis connection needs to be open.")) - }) - - it("errors when called on a new client", async () => - expect(async () => await client.hsetall('foo', { foo: 'bar', baz: 'qux' })) - .rejects.toThrowErrorOfType(RedisOmError, "Redis connection needs to be open.")) - }) -}) diff --git a/spec/unit/client/client-jsonget.spec.ts b/spec/unit/client/client-jsonget.spec.ts deleted file mode 100644 index db1b031..0000000 --- a/spec/unit/client/client-jsonget.spec.ts +++ /dev/null @@ -1,47 +0,0 @@ -import '../../helpers/custom-matchers' - -import { json } from '../helpers/mock-redis' - -import { Client, RedisJsonData } from '$lib/client' -import { RedisOmError } from '$lib/error' - -describe("Client", () => { - - let client: Client - let result: RedisJsonData | null - - beforeEach(() => { client = new Client() }) - - describe("#jsonget", () => { - describe("when called on an open client", () => { - beforeEach(async () => { - await client.open() - json.get.mockResolvedValue([ { "foo": "bar", "bar": 42, "baz": true, "qux": null } ]) - result = await client.jsonget('foo') - }) - - it("passes the command to redis", async () => { - expect(json.get).toHaveBeenCalledWith('foo', { path: '$' }) - }) - - it("returns the JSON", async () => { - expect(result).toEqual({ foo: 'bar', bar: 42, baz: true, qux: null }) - }) - }) - - describe("when called on a closed client", () => { - beforeEach(async () => { - await client.open() - await client.close() - }) - - it("errors when called on a closed client", () => - expect(async () => await client.jsonget('foo')) - .rejects.toThrowErrorOfType(RedisOmError, "Redis connection needs to be open.")) - }) - - it("errors when called on a new client", async () => - expect(async () => await client.jsonget('foo')) - .rejects.toThrowErrorOfType(RedisOmError, "Redis connection needs to be open.")) - }) -}) diff --git a/spec/unit/client/client-jsonset.spec.ts b/spec/unit/client/client-jsonset.spec.ts deleted file mode 100644 index 27db497..0000000 --- a/spec/unit/client/client-jsonset.spec.ts +++ /dev/null @@ -1,41 +0,0 @@ -import '../../helpers/custom-matchers' - -import { json } from '../helpers/mock-redis' - -import { Client } from '$lib/client' -import { RedisOmError } from '$lib/error' - -describe("Client", () => { - - let client: Client - - beforeEach(() => { client = new Client() }) - - describe("#jsonset", () => { - describe("when called on an open client", () => { - beforeEach(async () => { - await client.open() - await client.jsonset('foo', { foo: 'bar', bar: 42, baz: true, qux: null }) - }) - - it("passes the command to redis", async () => { - expect(json.set).toHaveBeenCalledWith('foo', '$', { foo: 'bar', bar: 42, baz: true, qux: null } ) - }) - }) - - describe("when called on a closed client", () => { - beforeEach(async () => { - await client.open() - await client.close() - }) - - it("errors when called on a closed client", () => - expect(async () => await client.jsonget('foo')) - .rejects.toThrowErrorOfType(RedisOmError, "Redis connection needs to be open.")) - }) - - it("errors when called on a new client", async () => - expect(async () => await client.jsonget('foo')) - .rejects.toThrowErrorOfType(RedisOmError, "Redis connection needs to be open.")) - }) -}) diff --git a/spec/unit/client/client-open.spec.ts b/spec/unit/client/client-open.spec.ts deleted file mode 100644 index 42644af..0000000 --- a/spec/unit/client/client-open.spec.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { redis, createClient } from '../helpers/mock-redis' -import { Client } from '$lib/client' - -describe("Client", () => { - - let client: Client, self: Client - - beforeEach(() => { client = new Client() }) - - describe("#open", () => { - describe("when not called", () => { - it("is not open", () => { - expect(client.isOpen()).toBe(false) - }) - }) - - describe("when called without a url", () => { - beforeEach(async () => {self = await client.open()}) - - it("creates a redis client with the default url", () => { - expect(createClient).toHaveBeenCalledWith({ url: 'redis://localhost:6379' }) - }) - - it("connects to redis", async () => { - expect(redis.connect).toHaveBeenCalled() - }) - - it("is open", () => { - expect(client.isOpen()).toBe(true) - }) - - it("returns itself", async () => { - expect(self).toBe(client) - }) - - describe("when trying to call it again", () => { - beforeEach(async () => {self = await client.open()}) - - it("doesn't re-create a redis client", () => { - expect(createClient).toBeCalledTimes(1) - }) - - it("doesn't open redis again", async () => { - expect(redis.connect).toBeCalledTimes(1) - }) - - it("returns itself", async () => { - expect(self).toBe(client) - }) - }) - }) - - describe("when called with a url", () => { - beforeEach(async () => {self = await client.open('foo')}) - - it("creates a new redis client with the provided url", () => { - expect(createClient).toHaveBeenCalledWith({ url: 'foo' }) - }) - - it("connects to redis", async () => { - expect(redis.connect).toHaveBeenCalled() - }) - - it("is open", () => { - expect(client.isOpen()).toBe(true) - }) - - it("returns itself", async () => { - expect(self).toBe(client) - }) - - describe("when trying to call it again", () => { - beforeEach(async () => {self = await client.open('foo')}) - - it("doesn't re-create a redis client", () => { - expect(createClient).toBeCalledTimes(1) - }) - - it("doesn't open redis again", async () => { - expect(redis.connect).toBeCalledTimes(1) - }) - - it("returns itself", async () => { - expect(self).toBe(client) - }) - }) - }) - }) -}) diff --git a/spec/unit/client/client-search.spec.ts b/spec/unit/client/client-search.spec.ts deleted file mode 100644 index 53028ba..0000000 --- a/spec/unit/client/client-search.spec.ts +++ /dev/null @@ -1,46 +0,0 @@ -import '../../helpers/custom-matchers' - -import { redis } from '../helpers/mock-redis' - -import { Client } from '$lib/client' -import { RedisOmError } from '$lib/error' - -describe("Client", () => { - - let client: Client - - beforeEach(() => { client = new Client() }) - - describe("#search", () => { - describe("when called on an open client", () => { - beforeEach(async () => { - await client.open() - }) - - it("sends the expected command when given minimal options", async () => { - await client.search('index', 'query') - expect(redis.ft.search).toHaveBeenCalledWith('index', 'query') - }) - - it("sends the expected command when given some options", async () => { - await client.search('index', 'query', { LIMIT: { from: 0, size: 5 } }) - expect(redis.ft.search).toHaveBeenCalledWith('index', 'query', { LIMIT: { from: 0, size: 5 } }) - }) - }) - - describe("when called on an unopened client", () => { - it("throws an error", async () => expect(async () => await client.search('index', 'query')) - .rejects.toThrowErrorOfType(RedisOmError, "Redis connection needs to be open.")) - }) - - describe("when called on a closed client", () => { - beforeEach(async () => { - await client.open() - await client.close() - }) - - it("throws an error", () => expect(async () => await client.search('index', 'query')) - .rejects.toThrowErrorOfType(RedisOmError, "Redis connection needs to be open.")) - }) - }) -}) diff --git a/spec/unit/client/client-unlink.spec.ts b/spec/unit/client/client-unlink.spec.ts deleted file mode 100644 index 7aceaef..0000000 --- a/spec/unit/client/client-unlink.spec.ts +++ /dev/null @@ -1,51 +0,0 @@ -import '../../helpers/custom-matchers' - -import { redis } from '../helpers/mock-redis' - -import { Client } from '$lib/client' -import { RedisOmError } from '$lib/error' - -describe("Client", () => { - - let client: Client - - beforeEach(() => { client = new Client() }) - - describe("#unlink", () => { - describe("when called on an open client", () => { - beforeEach(async () => { - await client.open() - }) - - it("doesn't call redis when passed no keys", async () => { - await client.unlink() - expect(redis.unlink).not.toHaveBeenCalled() - }) - - it("passes a single key to redis", async () => { - await client.unlink('foo') - expect(redis.unlink).toHaveBeenCalledWith(expect.arrayContaining(['foo'])) - }) - - it("passes multiple keys to redis", async () => { - await client.unlink('foo', 'bar', 'baz') - expect(redis.unlink).toHaveBeenCalledWith(expect.arrayContaining(['foo', 'bar', 'baz'])) - }) - }) - - describe("when called on a closed client", () => { - beforeEach(async () => { - await client.open() - await client.close() - }) - - it("errors when called on a closed client", () => - expect(async () => await client.unlink('foo')) - .rejects.toThrowErrorOfType(RedisOmError, "Redis connection needs to be open.")) - }) - - it("errors when called on a new client", async () => - expect(async () => await client.unlink('foo')) - .rejects.toThrowErrorOfType(RedisOmError, "Redis connection needs to be open.")) - }) -}) diff --git a/spec/unit/client/client-use.spec.ts b/spec/unit/client/client-use.spec.ts deleted file mode 100644 index fe5ed6a..0000000 --- a/spec/unit/client/client-use.spec.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { redis, createClient } from '../helpers/mock-redis' -import { Client } from '$lib/client' - -const BOGUS_CONNECTION = { THIS_IS_NOT: 'a real connection' } - - -describe("Client", () => { - - let client: Client, self: Client - - beforeEach(() => { client = new Client() }) - - describe("#use", () => { - describe("when not called", () => { - it("is not open", () => { - expect(client.isOpen()).toBe(false) - }) - }) - - describe("when called", () => { - beforeEach(async () => { - // @ts-ignore: no way to call createClient without actually connecting to Redis - self = await client.use(BOGUS_CONNECTION) - }) - - it("creates a redis client with the connection", () => { - expect(createClient).not.toHaveBeenCalled() - }) - - it("is open", () => { - expect(client.isOpen()).toBe(true) - }) - - it("returns itself", async () => { - expect(self).toBe(client) - }) - }) - - describe("when called on an open connection", () => { - beforeEach(async () => { - await client.open() - // @ts-ignore: no way to call createClient without actually connecting to Redis - self = await client.use(BOGUS_CONNECTION) - }) - - it("closes the existing redis connection", () => { - expect(redis.quit).toHaveBeenCalled() - }) - - it("doesn't create a new redis client", () => { - expect(createClient).not.toHaveBeenCalledWith() - }) - - it("is open", () => { - expect(client.isOpen()).toBe(true) - }) - - it("returns itself", async () => { - expect(self).toBe(client) - }) - }) - }) -}) diff --git a/spec/unit/error/errors.spec.ts b/spec/unit/error/errors.spec.ts deleted file mode 100644 index b80c264..0000000 --- a/spec/unit/error/errors.spec.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { ArrayHashInput, FieldNotInSchema, InvalidHashInput, InvalidHashValue, InvalidInput, InvalidJsonInput, InvalidJsonValue, InvalidSchema, InvalidValue, NestedHashInput, NullJsonInput, NullJsonValue, PointOutOfRange, RedisOmError, SearchError, SemanticSearchError } from '$lib/error' -import { Field } from '$lib/schema' - -describe("Errors", () => { - - let error: any - - describe.each([ - ["RedisOmError", RedisOmError], - ["InvalidInput", InvalidInput], - ["InvalidSchema", InvalidSchema], - ["InvalidValue", InvalidValue], - ["SearchError", SearchError], - ["SemanticSearchError", SemanticSearchError] - ])("%s", (_, errorClass) => { - beforeEach(() => { error = new errorClass("An error has occured.") }) - it("has the expected message", () => expect(error.message).toBe(`An error has occured.`)) - }) - - describe.each([ - ["NullJsonInput", NullJsonInput, `Null or undefined found in field 'aString' of type 'string' in JSON at "$.aString".`], - ["InvalidJsonInput", InvalidJsonInput, `Unexpected value for field 'aString' of type 'string' in JSON at "$.aString".`], - ["NullJsonValue", NullJsonValue, `Null or undefined found in field 'aString' of type 'string' from JSON path "$.aString" in Redis.`], - ["InvalidJsonValue", InvalidJsonValue, `Unexpected value for field 'aString' of type 'string' from JSON path "$.aString" in Redis.`] - ])("%s", (_, errorClass, expectedMessage) => { - beforeEach(() => { error = new errorClass(new Field('aString', { type: 'string' })) }) - it("has the expected message", () => expect(error.message).toBe(expectedMessage)) - it("has the expected field name", () => expect(error.fieldName).toBe('aString')) - it("has the expected field type", () => expect(error.fieldType).toBe('string')) - it("has the expected JSON path", () => expect(error.jsonPath).toBe('$.aString')) - }) - - describe.each([ - ["NestedHashInput", NestedHashInput, `Unexpected object in Hash at property 'aString'. You can not store a nested object in a Redis Hash.`], - ["ArrayHashInput", ArrayHashInput, `Unexpected array in Hash at property 'aString'. You can not store an array in a Redis Hash without defining it in the Schema.`], - ["FieldNotInSchema", FieldNotInSchema, `The field 'aString' is not part of the schema and thus cannot be used to search.`] - ])("%s", (_, errorClass, expectedMessage) => { - beforeEach(() => { error = new errorClass('aString') }) - it("has the expected message", () => expect(error.message).toBe(expectedMessage)) - it("has the expected field", () => expect(error.field).toBe('aString')) - }) - - describe("InvalidHashInput", () => { - beforeEach(() => { error = new InvalidHashInput(new Field('aString', { type: 'string' })) }) - it("has the expected message", () => expect(error.message).toBe(`Unexpected value for field 'aString' of type 'string' in Hash.`)) - it("has the expected field name", () => expect(error.fieldName).toBe('aString')) - it("has the expected field type", () => expect(error.fieldType).toBe('string')) - }) - - describe("InvalidHashValue", () => { - beforeEach(() => { error = new InvalidHashValue(new Field('aString', { type: 'string' })) }) - it("has the expected message", () => expect(error.message).toBe(`Unexpected value for field 'aString' of type 'string' from Hash field "aString" read from Redis.`)) - it("has the expected field name", () => expect(error.fieldName).toBe('aString')) - it("has the expected field type", () => expect(error.fieldType).toBe('string')) - it("has the expected Hash field", () => expect(error.hashField).toBe('aString')) - }) - - describe("PointOutOfRange", () => { - beforeEach(() => { error = new PointOutOfRange({ longitude: 4242, latitude: 2323 }) }) - it("has the expected message", () => expect(error.message).toBe(`Points must be between ±85.05112878 latitude and ±180 longitude.`)) - it("has the expected point", () => expect(error.point).toEqual({ longitude: 4242, latitude: 2323 })) - }) -}) diff --git a/spec/unit/helpers/mock-client.ts b/spec/unit/helpers/mock-client.ts deleted file mode 100644 index 8635015..0000000 --- a/spec/unit/helpers/mock-client.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { vi } from 'vitest' - -import { Client } from '$lib/client' - -export const client = Object.create(Client.prototype) - -client.use = vi.fn() -client.useNoClose = vi.fn() -client.open = vi.fn() -client.close = vi.fn() -client.execute = vi.fn() -client.fetchRepository = vi.fn() -client.createIndex = vi.fn() -client.dropIndex = vi.fn() -client.search = vi.fn() -client.unlink = vi.fn() -client.expire = vi.fn() -client.get = vi.fn() -client.set = vi.fn() -client.hgetall = vi.fn() -client.hsetall = vi.fn() -client.jsonget = vi.fn() -client.jsonset = vi.fn() - -vi.mock('$lib/client', () => ({ - Client: vi.fn(() => client) -})) diff --git a/spec/unit/helpers/mock-indexer.ts b/spec/unit/helpers/mock-indexer.ts deleted file mode 100644 index 5fcbd70..0000000 --- a/spec/unit/helpers/mock-indexer.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { vi } from 'vitest' - -vi.mock('$lib/indexer', () => ({ - buildRediSearchSchema: vi.fn() -})) - diff --git a/spec/unit/helpers/mock-redis.ts b/spec/unit/helpers/mock-redis.ts deleted file mode 100644 index 5fcd0a8..0000000 --- a/spec/unit/helpers/mock-redis.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { vi } from 'vitest' - -export const ft = { - create: vi.fn(), - search: vi.fn(), - dropIndex: vi.fn() -} - -export const json = { - get: vi.fn(), - set: vi.fn() -} - -export const redis = { - ft, - json, - connect: vi.fn(), - quit: vi.fn(), - get: vi.fn(), - set: vi.fn(), - hGetAll: vi.fn(), - expire: vi.fn(), - sendCommand: vi.fn(), - unlink: vi.fn(), - multi: vi.fn().mockImplementation(() => multi) -} - -export const multi = { - unlink: vi.fn().mockImplementation(() => multi), - hSet: vi.fn().mockImplementation(() => multi), - exec: vi.fn().mockImplementation(() => redis) -} - -export const createClient = vi.fn(() => redis) - -vi.mock('redis', () => ({ createClient })) diff --git a/spec/unit/helpers/search-helpers.ts b/spec/unit/helpers/search-helpers.ts deleted file mode 100644 index e068425..0000000 --- a/spec/unit/helpers/search-helpers.ts +++ /dev/null @@ -1,323 +0,0 @@ -import { client } from '../helpers/mock-client' - -import { EntityId } from '$lib/entity' - -import { - A_NUMBER, ANOTHER_NUMBER, A_THIRD_NUMBER, - A_NUMBER_STRING, ANOTHER_NUMBER_STRING, A_THIRD_NUMBER_STRING, - A_POINT, ANOTHER_POINT, A_THIRD_POINT, - A_POINT_STRING, ANOTHER_POINT_STRING, A_THIRD_POINT_STRING, - A_STRING, ANOTHER_STRING, A_THIRD_STRING, - SOME_STRINGS, SOME_OTHER_STRINGS, SOME_MORE_STRINGS, - SOME_STRINGS_JOINED, SOME_OTHER_STRINGS_JOINED, SOME_MORE_STRINGS_JOINED, - SOME_STRINGS_JSON, SOME_OTHER_STRINGS_JSON, SOME_MORE_STRINGS_JSON, - SOME_TEXT, SOME_OTHER_TEXT, SOME_MORE_TEXT } from '../../helpers/example-data' - -export const SIMPLE_ENTITY_1 = { - [EntityId]: '1', - aString: A_STRING, - someText: SOME_TEXT, - aNumber: A_NUMBER, - aBoolean: false, - aPoint: A_POINT, - someStrings: SOME_STRINGS -} - -export const SIMPLE_ENTITY_2 = { - [EntityId]: '2', - aString: ANOTHER_STRING, - someText: SOME_OTHER_TEXT, - aNumber: ANOTHER_NUMBER, - aBoolean: true, - aPoint: ANOTHER_POINT, - someStrings: SOME_OTHER_STRINGS -} - -export const SIMPLE_ENTITY_3 = { - [EntityId]: '3', - aString: A_THIRD_STRING, - someText: SOME_MORE_TEXT, - aNumber: A_THIRD_NUMBER, - aBoolean: false, - aPoint: A_THIRD_POINT, - someStrings: SOME_MORE_STRINGS -} - -export const SIMPLE_ENTITY_4 = { - [EntityId]: '4', - aString: A_STRING, - someText: SOME_OTHER_TEXT, - aNumber: A_THIRD_NUMBER, - aBoolean: true, - aPoint: A_POINT, - someStrings: SOME_OTHER_STRINGS -} - -export const SIMPLE_ENTITY_5 = { - [EntityId]: '5', - aString: A_THIRD_STRING, - someText: SOME_TEXT, - aNumber: ANOTHER_NUMBER, - aBoolean: false, - aPoint: A_THIRD_POINT, - someStrings: SOME_STRINGS -} - -export function mockClientSearchToReturnNothing() { - vi.mocked(client.search).mockResolvedValue({ - total: 0, - documents: [] - }) -} - -export function mockClientSearchToReturnCountOf(count: number) { - vi.mocked(client.search).mockResolvedValue({ - total: count, - documents: [] - }) -} - -export function mockClientSearchToReturnSingleKey() { - vi.mocked(client.search) - .mockResolvedValueOnce({ - total: 1, - documents: [ - { id: 'SimpleHashEntity:1', value: {} } - ] - }) - .mockResolvedValue({ - total: 1, - documents: [] - }) -} - -export function mockClientSearchToReturnSingleHash() { - vi.mocked(client.search) - .mockResolvedValueOnce({ - total: 1, - documents: [ - { id: 'SimpleHashEntity:1', value: HASH_DATA_1 }] - }) - .mockResolvedValue({ - total: 1, - documents: [] - }) -} - -export function mockClientSearchToReturnSingleJsonString() { - vi.mocked(client.search) - .mockResolvedValueOnce({ - total: 1, - documents: [ - { id: 'SimpleJsonEntity:1', value: JSON_DATA_1 }] - }) - .mockResolvedValue({ - total: 1, - documents: [] - }) -} - -export function mockClientSearchToReturnMultipleKeys() { - vi.mocked(client.search) - .mockResolvedValueOnce({ - total: 3, - documents: [ - { id: 'SimpleHashEntity:1', value: {} }, - { id: 'SimpleHashEntity:2', value: {} }, - { id: 'SimpleHashEntity:3', value: {} }] - }) - .mockResolvedValue({ - total: 3, - documents: [] - }) -} - -export function mockClientSearchToReturnMultipleHashes() { - vi.mocked(client.search) - .mockResolvedValueOnce({ - total: 3, - documents: [ - { id: 'SimpleHashEntity:1', value: HASH_DATA_1 }, - { id: 'SimpleHashEntity:2', value: HASH_DATA_2 }, - { id: 'SimpleHashEntity:3', value: HASH_DATA_3 }] - }) - .mockResolvedValue({ - total: 3, - documents: [] - }) -} - -export function mockClientSearchToReturnMultipleJsonStrings() { - vi.mocked(client.search) - .mockResolvedValueOnce({ - total: 3, - documents: [ - { id: 'SimpleJsonEntity:1', value: JSON_DATA_1 }, - { id: 'SimpleJsonEntity:2', value: JSON_DATA_2 }, - { id: 'SimpleJsonEntity:3', value: JSON_DATA_3 }] - }) - .mockResolvedValue({ - total: 3, - documents: [] - }) -} - -export function mockClientSearchToReturnPaginatedKeys() { - vi.mocked(client.search) - .mockResolvedValueOnce({ - total: 5, - documents: [ - { id: 'SimpleHashEntity:1', value: {} }, - { id: 'SimpleHashEntity:2', value: {} }] - }) - .mockResolvedValueOnce({ - total: 5, - documents: [ - { id: 'SimpleHashEntity:3', value: {} }, - { id: 'SimpleHashEntity:4', value: {} }] - }) - .mockResolvedValueOnce({ - total: 5, - documents: [ - { id: 'SimpleHashEntity:5', value: {} }] - }) - .mockResolvedValue({ - total: 5, - documents: [] - }) -} - -export function mockClientSearchToReturnPaginatedHashes() { - vi.mocked(client.search) - .mockResolvedValueOnce({ - total: 5, - documents: [ - { id: 'SimpleHashEntity:1', value: HASH_DATA_1 }, - { id: 'SimpleHashEntity:2', value: HASH_DATA_2 }] - }) - .mockResolvedValueOnce({ - total: 5, - documents: [ - { id: 'SimpleHashEntity:3', value: HASH_DATA_3 }, - { id: 'SimpleHashEntity:4', value: HASH_DATA_4 }] - }) - .mockResolvedValueOnce({ - total: 5, - documents: [ - { id: 'SimpleHashEntity:5', value: HASH_DATA_5 }] - }) - .mockResolvedValue({ - total: 5, - documents: [] - }) -} - -export function mockClientSearchToReturnPaginatedJsonStrings() { - vi.mocked(client.search) - .mockResolvedValueOnce({ - total: 5, - documents: [ - { id: 'SimpleJsonEntity:1', value: JSON_DATA_1 }, - { id: 'SimpleJsonEntity:2', value: JSON_DATA_2 }] - }) - .mockResolvedValueOnce({ - total: 5, - documents: [ - { id: 'SimpleJsonEntity:3', value: JSON_DATA_3 }, - { id: 'SimpleJsonEntity:4', value: JSON_DATA_4 }] - }) - .mockResolvedValueOnce({ - total: 5, - documents: [ - { id: 'SimpleJsonEntity:5', value: JSON_DATA_5 }] - }) - .mockResolvedValue({ - total: 5, - documents: [] - }) -} - -const HASH_DATA_1 = { - aString: A_STRING, - someText: SOME_TEXT, - aNumber: A_NUMBER_STRING, - aBoolean: '0', - aPoint: A_POINT_STRING, - someStrings: SOME_STRINGS_JOINED } - -const HASH_DATA_2 = { - aString: ANOTHER_STRING, - someText: SOME_OTHER_TEXT, - aNumber: ANOTHER_NUMBER_STRING, - aBoolean: '1', - aPoint: ANOTHER_POINT_STRING, - someStrings: SOME_OTHER_STRINGS_JOINED } - -const HASH_DATA_3 = { - aString: A_THIRD_STRING, - someText: SOME_MORE_TEXT, - aNumber: A_THIRD_NUMBER_STRING, - aBoolean: '0', - aPoint: A_THIRD_POINT_STRING, - someStrings: SOME_MORE_STRINGS_JOINED } - -const HASH_DATA_4 = { - aString: A_STRING, - someText: SOME_OTHER_TEXT, - aNumber: A_THIRD_NUMBER_STRING, - aBoolean: '1', - aPoint: A_POINT_STRING, - someStrings: SOME_OTHER_STRINGS_JOINED } - -const HASH_DATA_5 = { - aString: A_THIRD_STRING, - someText: SOME_TEXT, - aNumber: ANOTHER_NUMBER_STRING, - aBoolean: '0', - aPoint: A_THIRD_POINT_STRING, - someStrings: SOME_STRINGS_JOINED } - -const JSON_DATA_1 = { - aString: A_STRING, - someText: SOME_TEXT, - aNumber: A_NUMBER, - aBoolean: false, - aPoint: A_POINT_STRING, - someStrings: SOME_STRINGS -} - -const JSON_DATA_2 = { - aString: ANOTHER_STRING, - someText: SOME_OTHER_TEXT, - aNumber: ANOTHER_NUMBER, - aBoolean: true, - aPoint: ANOTHER_POINT_STRING, - someStrings: SOME_OTHER_STRINGS -} - -const JSON_DATA_3 = { - aString: A_THIRD_STRING, - someText: SOME_MORE_TEXT, - aNumber: A_THIRD_NUMBER, - aBoolean: false, - aPoint: A_THIRD_POINT_STRING, - someStrings: SOME_MORE_STRINGS -} - -const JSON_DATA_4 = { - aString: A_STRING, - someText: SOME_OTHER_TEXT, - aNumber: A_THIRD_NUMBER, - aBoolean: true, - aPoint: A_POINT_STRING, - someStrings: SOME_OTHER_STRINGS -} - -const JSON_DATA_5 = { - aString: A_THIRD_STRING, - someText: SOME_TEXT, - aNumber: ANOTHER_NUMBER, - aBoolean: false, - aPoint: A_THIRD_POINT_STRING, - someStrings: SOME_STRINGS -} diff --git a/spec/unit/helpers/test-entity-and-schema.ts b/spec/unit/helpers/test-entity-and-schema.ts deleted file mode 100644 index 16f5002..0000000 --- a/spec/unit/helpers/test-entity-and-schema.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { Schema } from '$lib/schema/schema'; - -export const simpleSchema = new Schema("SimpleEntity", { - aString: { type: 'string' }, - someText: { type: 'text' }, - aNumber: { type: 'number' }, - aBoolean: { type: 'boolean' }, - aPoint: { type: 'point' }, - aDate: { type: 'date' }, - someStrings: { type: 'string[]' } -}); - -export const simpleHashSchema = new Schema("SimpleHashEntity", { - aString: { type: 'string' }, - someText: { type: 'text' }, - aNumber: { type: 'number' }, - aBoolean: { type: 'boolean' }, - aPoint: { type: 'point' }, - aDate: { type: 'date' }, - someStrings: { type: 'string[]' } -}, { - dataStructure: 'HASH' -}); - -export const simpleSortableHashSchema = new Schema("SimpleHashEntity", { - aString: { type: 'string', sortable: true }, - someText: { type: 'text', sortable: true }, - aNumber: { type: 'number', sortable: true }, - aBoolean: { type: 'boolean', sortable: true }, - aPoint: { type: 'point' }, - aDate: { type: 'date', sortable: true }, - someStrings: { type: 'string[]' } -}, { - dataStructure: 'HASH' -}); - -export const simpleJsonSchema = new Schema("SimpleJsonEntity", { - aString: { type: 'string' }, - someText: { type: 'text' }, - aNumber: { type: 'number' }, - aBoolean: { type: 'boolean' }, - aPoint: { type: 'point' }, - aDate: { type: 'date' }, - someStrings: { type: 'string[]' } -}, { - dataStructure: 'JSON' -}); - -export const simpleSortableJsonSchema = new Schema("SimpleJsonEntity", { - aString: { type: 'string', sortable: true }, - someText: { type: 'text', sortable: true }, - aNumber: { type: 'number', sortable: true }, - aBoolean: { type: 'boolean', sortable: true }, - aPoint: { type: 'point' }, - aDate: { type: 'date', sortable: true }, - someStrings: { type: 'string[]' } -}, { - dataStructure: 'JSON' -}); - -export const aliasedSchema = new Schema("AliasedEntity", { - aString: { type: 'string', alias: 'anotherString' }, - someText: { type: 'text', alias: 'someOtherText' }, - aNumber: { type: 'number', alias: 'anotherNumber' }, - aBoolean: { type: 'boolean', alias: 'anotherBoolean' }, - aPoint: { type: 'point', alias: 'anotherPoint' }, - aDate: { type: 'date', alias: 'anotherDate' }, - someStrings: { type: 'string[]', alias: 'someOtherStrings' } -}); - -export const stopWordsOffSchema = new Schema("StopWordsOffEntity", { - someText: { type: 'text' } -}, { - useStopWords: 'OFF' -}); - -export const customStopWordsSchema = new Schema("CustomStopWordsEntity", { - someText: { type: 'text' } -}, { - useStopWords: 'CUSTOM', - stopWords: ['foo', 'bar', 'baz'] -}); diff --git a/spec/unit/indexer/boolean-hash-fields.spec.ts b/spec/unit/indexer/boolean-hash-fields.spec.ts deleted file mode 100644 index 3bee618..0000000 --- a/spec/unit/indexer/boolean-hash-fields.spec.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { Schema, SchemaDefinition, DataStructure } from '$lib/schema' -import { buildRediSearchSchema } from '$lib/indexer' - - -describe("#buildRediSearchSchema", () => { - describe.each([ - - ["that defines an unconfigured boolean for a HASH", { - schemaDef: { aField: { type: 'boolean' } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { aField: { type: 'TAG', AS: 'aField' } } - }], - - ["that defines an aliased boolean for a HASH", { - schemaDef: { aField: { type: 'boolean', alias: 'anotherField' } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { anotherField: { type: 'TAG', AS: 'aField' } } - }], - - ["that defines a fielded boolean for a HASH", { - schemaDef: { aField: { type: 'boolean', field: 'anotherField' } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { anotherField: { type: 'TAG', AS: 'aField' } } - }], - - ["that defines a sorted boolean for a HASH", { - schemaDef: { aField: { type: 'boolean', sortable: true } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { aField: { type: 'TAG', AS: 'aField', SORTABLE: true } } - }], - - ["that defines an unsorted boolean for a HASH", { - schemaDef: { aField: { type: 'boolean', sortable: false } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { aField: { type: 'TAG', AS: 'aField' } } - }], - - ["that defines an indexed boolean for a HASH", { - schemaDef: { aField: { type: 'boolean', indexed: true } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { aField: { type: 'TAG', AS: 'aField' } } - }], - - ["that defines an unindexed boolean for a HASH", { - schemaDef: { aField: { type: 'boolean', indexed: false } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { aField: { type: 'TAG', AS: 'aField', NOINDEX: true } } - }], - - ["that defines a fully configured boolean for a HASH", { - schemaDef: { aField: { type: 'boolean', alias: 'ignoredField', field: 'anotherField', sortable: true, indexed: false } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { anotherField: { type: 'TAG', AS: 'aField', SORTABLE: true, NOINDEX: true } } - }] - - ])("%s", (_, data) => { - it("generates a Redis schema for the field", () => { - let schemaDef = data.schemaDef - let dataStructure = data.dataStructure as DataStructure - let expectedRedisSchema = data.expectedRedisSchema - - let schema = new Schema('TestEntity', schemaDef, { dataStructure }) - let actual = buildRediSearchSchema(schema) - expect(actual).toEqual(expectedRedisSchema) - }) - }) -}) diff --git a/spec/unit/indexer/boolean-json-fields.spec.ts b/spec/unit/indexer/boolean-json-fields.spec.ts deleted file mode 100644 index 34eb585..0000000 --- a/spec/unit/indexer/boolean-json-fields.spec.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { RediSearchSchema } from 'redis' - -import { Schema, SchemaDefinition, DataStructure } from '$lib/schema' -import { buildRediSearchSchema } from '$lib/indexer' - - -const warnSpy = vi.spyOn(global.console, 'warn').mockImplementation(() => {}) - -describe("#buildRediSearchSchema", () => { - describe.each([ - - ["that defines an unconfigured boolean for a JSON", { - schemaDef: { aField: { type: 'boolean' } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.aField': { AS: 'aField', type: 'TAG' } }, - expectedWarning: null - }], - - ["that defines an aliased boolean for a JSON", { - schemaDef: { aField: { type: 'boolean', alias: 'anotherField' } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.anotherField': { AS: 'aField', type: 'TAG' } }, - expectedWarning: null - }], - - ["that defines a pathed boolean for a JSON", { - schemaDef: { aField: { type: 'boolean', path: '$.anotherField' } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.anotherField': { AS: 'aField', type: 'TAG' } }, - expectedWarning: null - }], - - ["that defines a sorted boolean for a JSON", { - schemaDef: { aField: { type: 'boolean', sortable: true } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.aField': { AS: 'aField', type: 'TAG' } }, - expectedWarning: "You have marked a boolean field as sortable but RediSearch doesn't support the SORTABLE argument on a TAG for JSON. Ignored." - }], - - ["that defines an unsorted boolean for a JSON", { - schemaDef: { aField: { type: 'boolean', sortable: false } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.aField': { AS: 'aField', type: 'TAG' } }, - expectedWarning: null - }], - - ["that defines an indexed boolean for a JSON", { - schemaDef: { aField: { type: 'boolean', indexed: true } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.aField': { AS: 'aField', type: 'TAG' } }, - expectedWarning: null - }], - - ["that defines an unidexed boolean for a JSON", { - schemaDef: { aField: { type: 'boolean', indexed: false } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.aField': { AS: 'aField', type: 'TAG', NOINDEX: true } }, - expectedWarning: null - }], - - ["that defines a fully configured boolean for a JSON", { - schemaDef: { aField: { type: 'boolean', alias: 'ignoredField', path: '$.anotherField', sortable: true, indexed: false } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.anotherField': { AS: 'aField', type: 'TAG', NOINDEX: true } }, - expectedWarning: "You have marked a boolean field as sortable but RediSearch doesn't support the SORTABLE argument on a TAG for JSON. Ignored." - }] - - ])("%s", (_, data) => { - - let redisSchema: RediSearchSchema - let schemaDef = data.schemaDef - let dataStructure = data.dataStructure as DataStructure - let expectedRedisSchema = data.expectedRedisSchema - let expectedWarning = data.expectedWarning - - beforeEach(() => { - warnSpy.mockReset() - let schema = new Schema('TestEntity', schemaDef, { dataStructure }) - redisSchema = buildRediSearchSchema(schema) - }) - - it("generates a Redis schema for the field", () => { - expect(redisSchema).toEqual(expectedRedisSchema) - }) - - if (expectedWarning) { - it("generates the expected warning", () => { - expect(warnSpy).toHaveBeenCalledWith(expectedWarning) - }) - } else { - it("does not generate a warning", () => { - expect(warnSpy).not.toHaveBeenCalled() - }) - } - - }) -}) diff --git a/spec/unit/indexer/date-hash-fields.spec.ts b/spec/unit/indexer/date-hash-fields.spec.ts deleted file mode 100644 index 251aca6..0000000 --- a/spec/unit/indexer/date-hash-fields.spec.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { Schema, SchemaDefinition, DataStructure } from '$lib/schema' -import { buildRediSearchSchema } from '$lib/indexer' - - -describe("#buildRediSearchSchema", () => { - describe.each([ - - ["that defines an unconfigured date for a HASH", { - schemaDef: { aField: { type: 'date' } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { aField: { type: 'NUMERIC', AS: 'aField' } } - }], - - ["that defines an aliased date for a HASH", { - schemaDef: { aField: { type: 'date', alias: 'anotherField' } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { anotherField: { type: 'NUMERIC', AS: 'aField' } } - }], - - ["that defines a fielded date for a HASH", { - schemaDef: { aField: { type: 'date', field: 'anotherField' } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { anotherField: { type: 'NUMERIC', AS: 'aField' } } - }], - - ["that defines a sorted date for a HASH", { - schemaDef: { aField: { type: 'date', sortable: true } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { aField: { type: 'NUMERIC', AS: 'aField', SORTABLE: true } } - }], - - ["that defines an unsorted date for a HASH", { - schemaDef: { aField: { type: 'date', sortable: false } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { aField: { type: 'NUMERIC', AS: 'aField' } } - }], - - ["that defines an indexed date for a HASH", { - schemaDef: { aField: { type: 'date', indexed: true } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { aField: { type: 'NUMERIC', AS: 'aField' } } - }], - - ["that defines an unindexed date for a HASH", { - schemaDef: { aField: { type: 'date', indexed: false } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { aField: { type: 'NUMERIC', AS: 'aField', NOINDEX: true } } - }], - - ["that defines a fully configured date for a HASH", { - schemaDef: { aField: { type: 'date', alias: 'ignoredField', field: 'anotherField', sortable: true, indexed: false } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { anotherField: { type: 'NUMERIC', AS: 'aField', SORTABLE: true, NOINDEX: true } } - }] - - ])("%s", (_, data) => { - it("generates a Redis schema for the field", () => { - let schemaDef = data.schemaDef - let dataStructure = data.dataStructure as DataStructure - let expectedRedisSchema = data.expectedRedisSchema - - let schema = new Schema('TestEntity', schemaDef, { dataStructure }) - let actual = buildRediSearchSchema(schema) - expect(actual).toEqual(expectedRedisSchema) - }) - }) -}) diff --git a/spec/unit/indexer/date-json-fields.spec.ts b/spec/unit/indexer/date-json-fields.spec.ts deleted file mode 100644 index ed19ad2..0000000 --- a/spec/unit/indexer/date-json-fields.spec.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { Schema, SchemaDefinition, DataStructure } from '$lib/schema' -import { buildRediSearchSchema } from '$lib/indexer' - - -describe("#buildRediSearchSchema", () => { - describe.each([ - - ["that defines an unconfigured date for a JSON", { - schemaDef: { aField: { type: 'date' } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.aField': { AS: 'aField', type: 'NUMERIC' } } - }], - - ["that defines an aliased date for a JSON", { - schemaDef: { aField: { type: 'date', alias: 'anotherField' } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.anotherField': { AS: 'aField', type: 'NUMERIC' } } - }], - - ["that defines an pathed date for a JSON", { - schemaDef: { aField: { type: 'date', path: '$.anotherField' } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.anotherField': { AS: 'aField', type: 'NUMERIC' } } - }], - - ["that defines a sorted date for a JSON", { - schemaDef: { aField: { type: 'date', sortable: true } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.aField': { AS: 'aField', type: 'NUMERIC', SORTABLE: true } } - }], - - ["that defines an unsorted date for a JSON", { - schemaDef: { aField: { type: 'date', sortable: false } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.aField': { AS: 'aField', type: 'NUMERIC' } } - }], - - ["that defines an indexed date for a JSON", { - schemaDef: { aField: { type: 'date', indexed: true } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.aField': { AS: 'aField', type: 'NUMERIC' } } - }], - - ["that defines an indexed date for a JSON", { - schemaDef: { aField: { type: 'date', indexed: false } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.aField': { AS: 'aField', type: 'NUMERIC', NOINDEX: true } } - }], - - ["that defines a fully configured date for a JSON", { - schemaDef: { aField: { type: 'date', alias: 'ignoredField', path: '$.anotherField', sortable: true, indexed: false } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.anotherField': { AS: 'aField', type: 'NUMERIC', SORTABLE: true, NOINDEX: true } } - }] - - ])("%s", (_, data) => { - it("generates a Redis schema for the field", () => { - let schemaDef = data.schemaDef - let dataStructure = data.dataStructure as DataStructure - let expectedRedisSchema = data.expectedRedisSchema - - let schema = new Schema('TestEntity', schemaDef, { dataStructure }) - let actual = buildRediSearchSchema(schema) - expect(actual).toEqual(expectedRedisSchema) - }) - }) -}) diff --git a/spec/unit/indexer/index-builder.spec.ts b/spec/unit/indexer/index-builder.spec.ts deleted file mode 100644 index fe0f1f9..0000000 --- a/spec/unit/indexer/index-builder.spec.ts +++ /dev/null @@ -1,103 +0,0 @@ -import { Schema, SchemaDefinition, DataStructure } from '$lib/schema' -import { buildRediSearchSchema } from '$lib/indexer' - - -describe("#buildRediSearchSchema", () => { - describe.each([ - - ["that is given an empty schema for a HASH", { - schemaDef: {} as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: {} - }], - - ["that is given an empty schema for JSON", { - schemaDef: {} as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: {} - }], - - ["that is given a well populated schema for a HASH", { - schemaDef: { - aString: { type: 'string' }, - anotherString: { type: 'string' }, - someText: { type: 'text' }, - someOtherText: { type: 'text' }, - aNumber: { type: 'number' }, - anotherNumber: { type: 'number' }, - aBoolean: { type: 'boolean' }, - anotherBoolean: { type: 'boolean' }, - aPoint: { type: 'point' }, - anotherPoint: { type: 'point' }, - aDate: { type: 'date' }, - anotherDate: { type: 'date' }, - someStrings: { type: 'string[]' }, - someOtherStrings: { type: 'string[]' } - } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { - aString: { type: 'TAG', AS: 'aString', SEPARATOR: '|' }, - anotherString: { type: 'TAG', AS: 'anotherString', SEPARATOR: '|' }, - someText: { type: 'TEXT', AS: 'someText', }, - someOtherText: { type: 'TEXT', AS: 'someOtherText', }, - aNumber: { type: 'NUMERIC', AS: 'aNumber', }, - anotherNumber: { type: 'NUMERIC', AS: 'anotherNumber', }, - aBoolean: { type: 'TAG', AS: 'aBoolean', }, - anotherBoolean: { type: 'TAG', AS: 'anotherBoolean', }, - aPoint: { type: 'GEO', AS: 'aPoint', }, - anotherPoint: { type: 'GEO', AS: 'anotherPoint', }, - aDate: { type: 'NUMERIC', AS: 'aDate', }, - anotherDate: { type: 'NUMERIC', AS: 'anotherDate', }, - someStrings: { type: 'TAG', AS: 'someStrings', SEPARATOR: '|' }, - someOtherStrings: { type: 'TAG', AS: 'someOtherStrings', SEPARATOR: '|' } - } - }], - - ["that is given a well populated schema for a JSON", { - schemaDef: { - aString: { type: 'string' }, - anotherString: { type: 'string' }, - someText: { type: 'text' }, - someOtherText: { type: 'text' }, - aNumber: { type: 'number' }, - anotherNumber: { type: 'number' }, - aBoolean: { type: 'boolean' }, - anotherBoolean: { type: 'boolean' }, - aPoint: { type: 'point' }, - anotherPoint: { type: 'point' }, - aDate: { type: 'date' }, - anotherDate: { type: 'date' }, - someStrings: { type: 'string[]' }, - someOtherStrings: { type: 'string[]' } - } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { - '$.aString': { type: 'TAG', AS: 'aString', SEPARATOR: '|' }, - '$.anotherString': { type: 'TAG', AS: 'anotherString', SEPARATOR: '|' }, - '$.someText': { type: 'TEXT', AS: 'someText' }, - '$.someOtherText': { type: 'TEXT', AS: 'someOtherText' }, - '$.aNumber': { type: 'NUMERIC', AS: 'aNumber' }, - '$.anotherNumber': { type: 'NUMERIC', AS: 'anotherNumber' }, - '$.aBoolean': { type: 'TAG', AS: 'aBoolean' }, - '$.anotherBoolean': { type: 'TAG', AS: 'anotherBoolean' }, - '$.aPoint': { type: 'GEO', AS: 'aPoint' }, - '$.anotherPoint': { type: 'GEO', AS: 'anotherPoint' }, - '$.aDate': { type: 'NUMERIC', AS: 'aDate' }, - '$.anotherDate': { type: 'NUMERIC', AS: 'anotherDate' }, - '$.someStrings[*]': { type: 'TAG', AS: 'someStrings', SEPARATOR: '|' }, - '$.someOtherStrings[*]': { type: 'TAG', AS: 'someOtherStrings', SEPARATOR: '|' } - } - }] - - ])("%s", (_, data) => { - it("generates a Redis schema for the field", () => { - let schemaDef = data.schemaDef - let dataStructure = data.dataStructure as DataStructure - let expectedRedisSchema = data.expectedRedisSchema - - let schema = new Schema('TestEntity', schemaDef, { dataStructure }) - let actual = buildRediSearchSchema(schema) - expect(actual).toEqual(expectedRedisSchema) - }) - }) -}) diff --git a/spec/unit/indexer/numeric-hash-fields.spec.ts b/spec/unit/indexer/numeric-hash-fields.spec.ts deleted file mode 100644 index 4ff08ee..0000000 --- a/spec/unit/indexer/numeric-hash-fields.spec.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { Schema, SchemaDefinition, DataStructure } from '$lib/schema' -import { buildRediSearchSchema } from '$lib/indexer' - - -describe("#buildRediSearchSchema", () => { - describe.each([ - - ["that defines an unconfigured number for a HASH", { - schemaDef: { aField: { type: 'number' } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { aField: { type: 'NUMERIC', AS: 'aField' } } - }], - - ["that defines an aliased number for a HASH", { - schemaDef: { aField: { type: 'number', alias: 'anotherField' } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { anotherField: { type: 'NUMERIC', AS: 'aField' } } - }], - - ["that defines a fielded number for a HASH", { - schemaDef: { aField: { type: 'number', field: 'anotherField' } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { anotherField: { type: 'NUMERIC', AS: 'aField' } } - }], - - ["that defines a sorted number for a HASH", { - schemaDef: { aField: { type: 'number', sortable: true } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { aField: { type: 'NUMERIC', AS: 'aField', SORTABLE: true } } - }], - - ["that defines an unsorted number for a HASH", { - schemaDef: { aField: { type: 'number', sortable: false } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { aField: { type: 'NUMERIC', AS: 'aField' } } - }], - - ["that defines an indexed number for a HASH", { - schemaDef: { aField: { type: 'number', indexed: true } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { aField: { type: 'NUMERIC', AS: 'aField' } } - }], - - ["that defines an unindexed number for a HASH", { - schemaDef: { aField: { type: 'number', indexed: false } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { aField: { type: 'NUMERIC', AS: 'aField', NOINDEX: true } } - }], - - ["that defines a fully-configured number for a HASH", { - schemaDef: { aField: { type: 'number', alias: 'ignoredField', field: 'anotherField', sortable: true, indexed: false } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { anotherField: { type: 'NUMERIC', AS: 'aField', SORTABLE: true, NOINDEX: true } } - }] - - ])("%s", (_, data) => { - it("generates a Redis schema for the field", () => { - let schemaDef = data.schemaDef - let dataStructure = data.dataStructure as DataStructure - let expectedRedisSchema = data.expectedRedisSchema - - let schema = new Schema('TestEntity', schemaDef, { dataStructure }) - let actual = buildRediSearchSchema(schema) - expect(actual).toEqual(expectedRedisSchema) - }) - }) -}) diff --git a/spec/unit/indexer/numeric-json-fields.spec.ts b/spec/unit/indexer/numeric-json-fields.spec.ts deleted file mode 100644 index 3739665..0000000 --- a/spec/unit/indexer/numeric-json-fields.spec.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { Schema, SchemaDefinition, DataStructure } from '$lib/schema' -import { buildRediSearchSchema } from '$lib/indexer' - - -describe("#buildRediSearchSchema", () => { - describe.each([ - - ["that defines an unconfigured number for a JSON", { - schemaDef: { aField: { type: 'number' } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.aField' : { AS: 'aField', type: 'NUMERIC' } } - }], - - ["that defines an aliased number for a JSON", { - schemaDef: { aField: { type: 'number', alias: 'anotherField' } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.anotherField' : { AS: 'aField', type: 'NUMERIC' } } - }], - - ["that defines an pathed number for a JSON", { - schemaDef: { aField: { type: 'number', path: '$.anotherField' } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.anotherField' : { AS: 'aField', type: 'NUMERIC' } } - }], - - ["that defines a sorted number for a JSON", { - schemaDef: { aField: { type: 'number', sortable: true } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.aField' : { AS: 'aField', type: 'NUMERIC', SORTABLE: true } } - }], - - ["that defines an unsorted number for a JSON", { - schemaDef: { aField: { type: 'number', sortable: false } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.aField' : { AS: 'aField', type: 'NUMERIC' } } - }], - - ["that defines an indexed number for a JSON", { - schemaDef: { aField: { type: 'number', indexed: true } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.aField' : { AS: 'aField', type: 'NUMERIC' } } - }], - - ["that defines an unindexed number for a JSON", { - schemaDef: { aField: { type: 'number', indexed: false } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.aField' : { AS: 'aField', type: 'NUMERIC', NOINDEX: true } } - }], - - ["that defines a fully-configured number for a JSON", { - schemaDef: { aField: { type: 'number', alias: 'ignoredField', path: '$.anotherField', sortable: true, indexed: false } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.anotherField': { AS: 'aField', type: 'NUMERIC', SORTABLE: true, NOINDEX: true } } - }] - - ])("%s", (_, data) => { - it("generates a Redis schema for the field", () => { - let schemaDef = data.schemaDef - let dataStructure = data.dataStructure as DataStructure - let expectedRedisSchema = data.expectedRedisSchema - - let schema = new Schema('TestEntity', schemaDef, { dataStructure }) - let actual = buildRediSearchSchema(schema) - expect(actual).toEqual(expectedRedisSchema) - }) - }) -}) diff --git a/spec/unit/indexer/point-hash-fields.spec.ts b/spec/unit/indexer/point-hash-fields.spec.ts deleted file mode 100644 index 34b745c..0000000 --- a/spec/unit/indexer/point-hash-fields.spec.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { Schema, SchemaDefinition, DataStructure } from '$lib/schema' -import { buildRediSearchSchema } from '$lib/indexer' - - -describe("#buildRediSearchSchema", () => { - describe.each([ - - ["that defines an unconfigured point for a HASH", { - schemaDef: { aField: { type: 'point' } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { aField: { type: 'GEO', AS: 'aField' } } - }], - - ["that defines an aliased point for a HASH", { - schemaDef: { aField: { type: 'point', alias: 'anotherField' } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { anotherField: { type: 'GEO', AS: 'aField' } } - }], - - ["that defines a fielded point for a HASH", { - schemaDef: { aField: { type: 'point', field: 'anotherField' } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { anotherField: { type: 'GEO', AS: 'aField' } } - }], - - ["that defines an indexed point for a HASH", { - schemaDef: { aField: { type: 'point', indexed: true } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { aField: { type: 'GEO', AS: 'aField' } } - }], - - ["that defines an unindexed point for a HASH", { - schemaDef: { aField: { type: 'point', indexed: false } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { aField: { type: 'GEO', AS: 'aField', NOINDEX: true } } - }], - - ["that defines a fully-configured point for a HASH", { - schemaDef: { aField: { type: 'point', alias: 'ignoredField', field: 'anotherField', indexed: false } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { anotherField: { type: 'GEO', AS: 'aField', NOINDEX: true } } - }] - - ])("%s", (_, data) => { - it("generates a Redis schema for the field", () => { - let schemaDef = data.schemaDef - let dataStructure = data.dataStructure as DataStructure - let expectedRedisSchema = data.expectedRedisSchema - - let schema = new Schema('TestEntity', schemaDef, { dataStructure }) - let actual = buildRediSearchSchema(schema) - expect(actual).toEqual(expectedRedisSchema) - }) - }) -}) diff --git a/spec/unit/indexer/point-json-fields.spec.ts b/spec/unit/indexer/point-json-fields.spec.ts deleted file mode 100644 index 68b9079..0000000 --- a/spec/unit/indexer/point-json-fields.spec.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { Schema, SchemaDefinition, DataStructure } from '$lib/schema' -import { buildRediSearchSchema } from '$lib/indexer' - - -describe("#buildRediSearchSchema", () => { - describe.each([ - - ["that defines an unconfigured point for a JSON", { - schemaDef: { aField: { type: 'point' } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.aField': { AS: 'aField', type: 'GEO' } } - }], - - ["that defines an aliased point for a JSON", { - schemaDef: { aField: { type: 'point', alias: 'anotherField' } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.anotherField': { AS: 'aField', type: 'GEO' } } - }], - - ["that defines a pathed point for a JSON", { - schemaDef: { aField: { type: 'point', path: '$.anotherField' } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.anotherField': { AS: 'aField', type: 'GEO' } } - }], - - ["that defines an indexed point for a JSON", { - schemaDef: { aField: { type: 'point', indexed: true } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.aField': { AS: 'aField', type: 'GEO' } } - }], - - ["that defines an unindexed point for a JSON", { - schemaDef: { aField: { type: 'point', indexed: false } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.aField': { AS: 'aField', type: 'GEO', NOINDEX: true } } - }], - - ["that defines a fully-configured point for a JSON", { - schemaDef: { aField: { type: 'point', alias: 'ignoredField', path: '$.anotherField', indexed: false } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.anotherField': { AS: 'aField', type: 'GEO', NOINDEX: true } } - }] - - - ])("%s", (_, data) => { - it("generates a Redis schema for the field", () => { - let schemaDef = data.schemaDef - let dataStructure = data.dataStructure as DataStructure - let expectedRedisSchema = data.expectedRedisSchema - - let schema = new Schema('TestEntity', schemaDef, { dataStructure }) - let actual = buildRediSearchSchema(schema) - expect(actual).toEqual(expectedRedisSchema) - }) - }) -}) diff --git a/spec/unit/indexer/string-array-hash-fields.spec.ts b/spec/unit/indexer/string-array-hash-fields.spec.ts deleted file mode 100644 index 7351c02..0000000 --- a/spec/unit/indexer/string-array-hash-fields.spec.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { Schema, SchemaDefinition, DataStructure } from '$lib/schema' -import { buildRediSearchSchema } from '$lib/indexer' - - -describe("#buildRediSearchSchema", () => { - describe.each([ - - ["that defines an unconfigured array for a HASH", { - schemaDef: { aField: { type: 'string[]' } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { aField: { AS: 'aField', type: 'TAG', SEPARATOR: '|' } } - }], - - ["that defines an aliased array for a HASH", { - schemaDef: { aField: { type: 'string[]', alias: 'anotherField' } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { anotherField: { AS: 'aField', type: 'TAG', SEPARATOR: '|' } } - }], - - ["that defines a fielded array for a HASH", { - schemaDef: { aField: { type: 'string[]', alias: 'anotherField' } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { anotherField: { AS: 'aField', type: 'TAG', SEPARATOR: '|' } } - }], - - ["that defines a separated array for a HASH", { - schemaDef: { aField: { type: 'string[]', separator: ',' } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { aField: { AS: 'aField', type: 'TAG', SEPARATOR: ',' } } - }], - - ["that defines an indexed array for a HASH", { - schemaDef: { aField: { type: 'string[]', indexed: true } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { aField: { AS: 'aField', type: 'TAG', SEPARATOR: '|' } } - }], - - ["that defines an unindexed array for a HASH", { - schemaDef: { aField: { type: 'string[]', indexed: false } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { aField: { AS: 'aField', type: 'TAG', SEPARATOR: '|', NOINDEX: true } } - }], - - ["that defines a fully-configured array for a HASH", { - schemaDef: { aField: { type: 'string[]', alias: 'ignoredField', field: 'anotherField', separator: ',', indexed: false } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { anotherField: { AS: 'aField', type: 'TAG', SEPARATOR: ',', NOINDEX: true } } - }] - - ])("%s", (_, data) => { - it("generates a Redis schema for the field", () => { - let schemaDef = data.schemaDef - let dataStructure = data.dataStructure as DataStructure - let expectedRedisSchema = data.expectedRedisSchema - - let schema = new Schema('TestEntity', schemaDef, { dataStructure }) - let actual = buildRediSearchSchema(schema) - expect(actual).toEqual(expectedRedisSchema) - }) - }) -}) diff --git a/spec/unit/indexer/string-array-json-fields.spec.ts b/spec/unit/indexer/string-array-json-fields.spec.ts deleted file mode 100644 index 738091d..0000000 --- a/spec/unit/indexer/string-array-json-fields.spec.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { Schema, SchemaDefinition, DataStructure } from '$lib/schema' -import { buildRediSearchSchema } from '$lib/indexer' - - -describe("#buildRediSearchSchema", () => { - describe.each([ - - ["that defines an unconfigured array for a JSON", { - schemaDef: { aField: { type: 'string[]' } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.aField[*]': { AS: 'aField', type: 'TAG', SEPARATOR: '|' } } - }], - - ["that defines an aliased array for a JSON", { - schemaDef: { aField: { type: 'string[]', alias: 'anotherField' } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.anotherField[*]': { AS: 'aField', type: 'TAG', SEPARATOR: '|' } } - }], - - ["that defines a pathed array for a JSON", { - schemaDef: { aField: { type: 'string[]', path: '$.anotherField[*]' } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.anotherField[*]': { AS: 'aField', type: 'TAG', SEPARATOR: '|' } } - }], - - ["that defines an indexed array for a JSON", { - schemaDef: { aField: { type: 'string[]', indexed: true } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.aField[*]': { AS: 'aField', type: 'TAG', SEPARATOR: '|' } } - }], - - ["that defines an unindexed array for a JSON", { - schemaDef: { aField: { type: 'string[]', indexed: false } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.aField[*]': { AS: 'aField', type: 'TAG', SEPARATOR: '|', NOINDEX: true } } - }], - - ["that defines a fully-configured array for a JSON", { - schemaDef: { aField: { type: 'string[]', alias: 'ignoredField', path: '$.anotherField[*]', indexed: false } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.anotherField[*]': { AS: 'aField', type: 'TAG', SEPARATOR: '|', NOINDEX: true } } - }] - - ])("%s", (_, data) => { - it("generates a Redis schema for the field", () => { - let schemaDef = data.schemaDef - let dataStructure = data.dataStructure as DataStructure - let expectedRedisSchema = data.expectedRedisSchema - - let schema = new Schema('TestEntity', schemaDef, { dataStructure }) - let actual = buildRediSearchSchema(schema) - expect(actual).toEqual(expectedRedisSchema) - }) - }) -}) diff --git a/spec/unit/indexer/string-hash-fields.spec.ts b/spec/unit/indexer/string-hash-fields.spec.ts deleted file mode 100644 index d73217b..0000000 --- a/spec/unit/indexer/string-hash-fields.spec.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { Schema, SchemaDefinition, DataStructure } from '$lib/schema' -import { buildRediSearchSchema } from '$lib/indexer' - - -describe("#buildRediSearchSchema", () => { - describe.each([ - - ["that defines an unconfigured string for a HASH", { - schemaDef: { aField: { type: 'string' } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { aField: { type: 'TAG', AS: 'aField', SEPARATOR: '|' } } - }], - - ["that defines an aliased string for a HASH", { - schemaDef: { aField: { type: 'string', alias: 'anotherField' } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { anotherField: { type: 'TAG', AS: 'aField', SEPARATOR: '|' } } - }], - - ["that defines a fielded string for a HASH", { - schemaDef: { aField: { type: 'string', field: 'anotherField' } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { anotherField: { type: 'TAG', AS: 'aField', SEPARATOR: '|' } } - }], - - ["that defines an unsorted string for a HASH", { - schemaDef: { aField: { type: 'string', sortable: false } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { aField: { type: 'TAG', AS: 'aField', SEPARATOR: '|' } } - }], - - ["that defines a sorted string for a HASH", { - schemaDef: { aField: { type: 'string', sortable: true } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { aField: { type: 'TAG', AS: 'aField', SEPARATOR: '|', SORTABLE: true } } - }], - - ["that defines a separated string for a HASH", { - schemaDef: { aField: { type: 'string', separator: ',' } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { aField: { type: 'TAG', AS: 'aField', SEPARATOR: ',' } } - }], - - ["that defines an indexed string for a HASH", { - schemaDef: { aField: { type: 'string', indexed: true } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { aField: { type: 'TAG', AS: 'aField', SEPARATOR: '|' } } - }], - - ["that defines an unindexed string for a HASH", { - schemaDef: { aField: { type: 'string', indexed: false } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { aField: { type: 'TAG', AS: 'aField', SEPARATOR: '|', NOINDEX: true } } - }], - - ["that defines a caseSensitive string for a HASH", { - schemaDef: { aField: { type: 'string', caseSensitive: true } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { aField: { type: 'TAG', AS: 'aField', SEPARATOR: '|', CASESENSITIVE: true } } - }], - - ["that defines a fully configured string for a HASH", { - schemaDef: { aField: { type: 'string', alias: 'ignoredField', field: 'anotherField', sortable: true, separator: ',', indexed: false, caseSensitive: true } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { anotherField: { type: 'TAG', AS: 'aField', SEPARATOR: ',', SORTABLE: true, NOINDEX: true, CASESENSITIVE: true } } - }] - - ])("%s", (_, data) => { - it("generates a Redis schema for the field", () => { - let schemaDef = data.schemaDef - let dataStructure = data.dataStructure as DataStructure - let expectedRedisSchema = data.expectedRedisSchema - - let schema = new Schema('TestEntity', schemaDef, { dataStructure }) - let actual = buildRediSearchSchema(schema) - expect(actual).toEqual(expectedRedisSchema) - }) - }) -}) diff --git a/spec/unit/indexer/string-json-fields.spec.ts b/spec/unit/indexer/string-json-fields.spec.ts deleted file mode 100644 index 030b05e..0000000 --- a/spec/unit/indexer/string-json-fields.spec.ts +++ /dev/null @@ -1,111 +0,0 @@ -import { RediSearchSchema } from 'redis' - -import { Schema, SchemaDefinition, DataStructure } from '$lib/schema' -import { buildRediSearchSchema } from '$lib/indexer' - - -const warnSpy = vi.spyOn(global.console, 'warn').mockImplementation(() => {}) - -describe("#buildRediSearchSchema", () => { - describe.each([ - - ["that defines an unconfigured string for a JSON", { - schemaDef: { aField: { type: 'string' } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.aField': { AS: 'aField', type: 'TAG', SEPARATOR: '|' } }, - expectedWarning: null - }], - - ["that defines an aliased string for a JSON", { - schemaDef: { aField: { type: 'string', alias: 'anotherField' } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.anotherField': { AS: 'aField', type: 'TAG', SEPARATOR: '|' } }, - expectedWarning: null - }], - - ["that defines a pathed string for a JSON", { - schemaDef: { aField: { type: 'string', path: '$.anotherField' } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.anotherField': { AS: 'aField', type: 'TAG', SEPARATOR: '|' } }, - expectedWarning: null - }], - - ["that defines an unsorted string for a JSON", { - schemaDef: { aField: { type: 'string', sortable: false } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.aField': { AS: 'aField', type: 'TAG', SEPARATOR: '|' } }, - expectedWarning: null - }], - - ["that defines a sorted string for a JSON", { - schemaDef: { aField: { type: 'string', sortable: true } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.aField': { AS: 'aField', type: 'TAG', SEPARATOR: '|' } }, - expectedWarning: "You have marked a string field as sortable but RediSearch doesn't support the SORTABLE argument on a TAG for JSON. Ignored." - }], - - ["that defines a separated string for a JSON", { - schemaDef: { aField: { type: 'string', separator: ',' } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.aField': { AS: 'aField', type: 'TAG', SEPARATOR: ',' } }, - expectedWarning: null - }], - - ["that defines an indexed string for a JSON", { - schemaDef: { aField: { type: 'string', indexed: true } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.aField': { AS: 'aField', type: 'TAG', SEPARATOR: '|' } }, - expectedWarning: null - }], - - ["that defines an unindexed string for a JSON", { - schemaDef: { aField: { type: 'string', indexed: false } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.aField': { AS: 'aField', type: 'TAG', SEPARATOR: '|', NOINDEX: true } }, - expectedWarning: null - }], - - ["that defines a caseSensitive string for a JSON", { - schemaDef: { aField: { type: 'string', caseSensitive: true } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.aField': { AS: 'aField', type: 'TAG', SEPARATOR: '|', CASESENSITIVE: true } }, - expectedWarning: null - }], - - ["that defines a fully configured string for a JSON", { - schemaDef: { aField: { type: 'string', alias: 'ignoredField', path: '$.anotherField', sortable: true, separator: ',', indexed: false, caseSensitive: true } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.anotherField': { AS: 'aField', type: 'TAG', SEPARATOR: ',', NOINDEX: true, CASESENSITIVE: true } }, - expectedWarning: "You have marked a string field as sortable but RediSearch doesn't support the SORTABLE argument on a TAG for JSON. Ignored." - }] - - ])("%s", (_, data) => { - - let redisSchema: RediSearchSchema - let schemaDef = data.schemaDef - let dataStructure = data.dataStructure as DataStructure - let expectedRedisSchema = data.expectedRedisSchema - let expectedWarning = data.expectedWarning - - beforeEach(() => { - warnSpy.mockReset() - let schema = new Schema('TestEntity', schemaDef, { dataStructure }) - redisSchema = buildRediSearchSchema(schema) - }) - - it("generates a Redis schema for the field", () => { - expect(redisSchema).toEqual(expectedRedisSchema) - }) - - if (expectedWarning) { - it("generates the expected warning", () => { - expect(warnSpy).toHaveBeenCalledWith(expectedWarning) - }) - } else { - it("does not generate a warning", () => { - expect(warnSpy).not.toHaveBeenCalled() - }) - } - - }) -}) diff --git a/spec/unit/indexer/text-hash-fields.spec.ts b/spec/unit/indexer/text-hash-fields.spec.ts deleted file mode 100644 index 9b5926b..0000000 --- a/spec/unit/indexer/text-hash-fields.spec.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { Schema, SchemaDefinition, DataStructure } from '$lib/schema' -import { buildRediSearchSchema } from '$lib/indexer' - - -describe("#buildRediSearchSchema", () => { - describe.each([ - - ["that defines an unconfigured text for a HASH", { - schemaDef: { aField: { type: 'text' } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { aField: { AS: 'aField', type: 'TEXT' } } - }], - - ["that defines an aliased text for a HASH", { - schemaDef: { aField: { type: 'text', alias: 'anotherField' } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { anotherField: { AS: 'aField', type: 'TEXT' } } - }], - - ["that defines a fielded text for a HASH", { - schemaDef: { aField: { type: 'text', field: 'anotherField' } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { anotherField: { AS: 'aField', type: 'TEXT' } } - }], - - ["that defines a sorted text for a HASH", { - schemaDef: { aField: { type: 'text', sortable: true } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { aField: { AS: 'aField', type: 'TEXT', SORTABLE: true } } - }], - - ["that defines an unsorted text for a HASH", { - schemaDef: { aField: { type: 'text', sortable: false } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { aField: { AS: 'aField', type: 'TEXT' } } - }], - - ["that defines an indexed text for a HASH", { - schemaDef: { aField: { type: 'text', indexed: true } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { aField: { AS: 'aField', type: 'TEXT' } } - }], - - ["that defines an unindexed text for a HASH", { - schemaDef: { aField: { type: 'text', indexed: false } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { aField: { AS: 'aField', type: 'TEXT', NOINDEX: true } } - }], - - ["that defines a phonetic matcher text for a HASH", { - schemaDef: { aField: { type: 'text', matcher: 'dm:en' } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { aField: { AS: 'aField', type: 'TEXT', PHONETIC: 'dm:en' } } - }], - - ["that defines an unstemmed text for a HASH", { - schemaDef: { aField: { type: 'text', stemming: false } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { aField: { AS: 'aField', type: 'TEXT', NOSTEM: true } } - }], - - ["that defines an unnormalized text for a HASH", { - schemaDef: { aField: { type: 'text', normalized: false } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { aField: { AS: 'aField', type: 'TEXT', SORTABLE: 'UNF' } } - }], - - ["that defines a weighted text for a HASH", { - schemaDef: { aField: { type: 'text', weight: 2 } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { aField: { AS: 'aField', type: 'TEXT', WEIGHT: 2 } } - }], - - ["that defines a fully configured text for a HASH", { - schemaDef: { aField: { type: 'text', alias: 'ignoredField', field: 'anotherField', sortable: true, indexed: false, matcher: 'dm:en', stemming: false, normalized: false, weight: 2 } } as SchemaDefinition, - dataStructure: 'HASH', - expectedRedisSchema: { anotherField: { AS: 'aField', type: 'TEXT', SORTABLE: 'UNF', NOINDEX: true, PHONETIC: 'dm:en', NOSTEM: true, WEIGHT: 2 } } - }] - - ])("%s", (_, data) => { - it("generates a Redis schema for the field", () => { - let schemaDef = data.schemaDef - let dataStructure = data.dataStructure as DataStructure - let expectedRedisSchema = data.expectedRedisSchema - - let schema = new Schema('TestEntity', schemaDef, { dataStructure }) - let actual = buildRediSearchSchema(schema) - expect(actual).toEqual(expectedRedisSchema) - }) - }) -}) diff --git a/spec/unit/indexer/text-json-fields.spec.ts b/spec/unit/indexer/text-json-fields.spec.ts deleted file mode 100644 index e8ef0c4..0000000 --- a/spec/unit/indexer/text-json-fields.spec.ts +++ /dev/null @@ -1,103 +0,0 @@ -import { Schema, SchemaDefinition, DataStructure } from '$lib/schema' -import { buildRediSearchSchema } from '$lib/indexer' - - -describe("#buildRediSearchSchema", () => { - describe.each([ - - ["that defines an unconfigured text for a JSON", { - schemaDef: { aField: { type: 'text' } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.aField': { AS: 'aField', type: 'TEXT' } } - }], - - ["that defines an aliased text for a JSON", { - schemaDef: { aField: { type: 'text', alias: 'anotherField' } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.anotherField': { AS: 'aField', type: 'TEXT' } } - }], - - ["that defines a pathed text for a JSON", { - schemaDef: { aField: { type: 'text', path: '$.anotherField' } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.anotherField': { AS: 'aField', type: 'TEXT' } } - }], - - ["that defines a sorted text for a JSON", { - schemaDef: { aField: { type: 'text', sortable: true } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.aField': { AS: 'aField', type: 'TEXT', SORTABLE: true } } - }], - - ["that defines an unsorted text for a JSON", { - schemaDef: { aField: { type: 'text', sortable: false } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.aField': { AS: 'aField', type: 'TEXT' } } - }], - - ["that defines an indexed text for a JSON", { - schemaDef: { aField: { type: 'text', indexed: true } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.aField': { AS: 'aField', type: 'TEXT' } } - }], - - ["that defines an unindexed text for a JSON", { - schemaDef: { aField: { type: 'text', indexed: false } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.aField': { AS: 'aField', type: 'TEXT', NOINDEX: true } } - }], - - ["that defines a phonetic matcher text for a JSON", { - schemaDef: { aField: { type: 'text', matcher: 'dm:en' } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.aField': { AS: 'aField', type: 'TEXT', PHONETIC: 'dm:en' } } - }], - - ["that defines a stemmed text for a JSON", { - schemaDef: { aField: { type: 'text', stemming: true } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.aField': { AS: 'aField', type: 'TEXT' } } - }], - - ["that defines an unstemmed text for a JSON", { - schemaDef: { aField: { type: 'text', stemming: false } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.aField': { AS: 'aField', type: 'TEXT', NOSTEM: true } } - }], - - ["that defines a normalized text for a JSON", { - schemaDef: { aField: { type: 'text', normalized: true } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.aField': { AS: 'aField', type: 'TEXT' } } - }], - - ["that defines an unnormalized text for a JSON", { - schemaDef: { aField: { type: 'text', normalized: false } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.aField': { AS: 'aField', type: 'TEXT', SORTABLE: 'UNF' } } - }], - - ["that defines a weighted text for a JSON", { - schemaDef: { aField: { type: 'text', weight: 2 } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.aField': { AS: 'aField', type: 'TEXT', WEIGHT: 2 } } - }], - - ["that defines a fully configured text for a JSON", { - schemaDef: { aField: { type: 'text', alias: 'ignoredField', path:'$.anotherField', sortable: true, indexed: false, matcher: 'dm:en', stemming: false, normalized: false, weight: 2 } } as SchemaDefinition, - dataStructure: 'JSON', - expectedRedisSchema: { '$.anotherField': { AS: 'aField', type: 'TEXT', SORTABLE: 'UNF', NOINDEX: true, PHONETIC: 'dm:en', NOSTEM: true, WEIGHT: 2 } } - }] - - ])("%s", (_, data) => { - it("generates a Redis schema for the field", () => { - let schemaDef = data.schemaDef - let dataStructure = data.dataStructure as DataStructure - let expectedRedisSchema = data.expectedRedisSchema - - let schema = new Schema('TestEntity', schemaDef, { dataStructure }) - let actual = buildRediSearchSchema(schema) - expect(actual).toEqual(expectedRedisSchema) - }) - }) -}) diff --git a/spec/unit/repository/repository-constructor.spec.ts b/spec/unit/repository/repository-constructor.spec.ts deleted file mode 100644 index 74307e8..0000000 --- a/spec/unit/repository/repository-constructor.spec.ts +++ /dev/null @@ -1,45 +0,0 @@ -import '../helpers/mock-client' -import '../helpers/mock-redis' - -import { createClient } from 'redis' - -import { Client, RedisConnection } from '$lib/client' -import { Repository } from '$lib/repository' - -import { simpleSchema } from '../helpers/test-entity-and-schema' - - -describe("Repository", () => { - - let client: Client - let connection: RedisConnection - let repository: Repository - - describe("when created with a Client", () => { - beforeEach(() => { - client = new Client() - repository = new Repository(simpleSchema, client) - }) - - it("uses the client it is given", () => { - // @ts-ignore - expect(repository.client).toBe(client) - }) - }) - - describe("when created with a RedisConnection", () => { - beforeEach(() => { - connection = createClient() - repository = new Repository(simpleSchema, connection) - }) - - it("creates a new client", () => { - expect(Client).toHaveBeenCalled() - }) - - it("uses the RedisConnection", () => { - // @ts-ignore - expect(repository.client.useNoClose).toHaveBeenCalledWith(connection) - }) - }) -}) diff --git a/spec/unit/repository/repository-create-index.spec.ts b/spec/unit/repository/repository-create-index.spec.ts deleted file mode 100644 index 8690e18..0000000 --- a/spec/unit/repository/repository-create-index.spec.ts +++ /dev/null @@ -1,145 +0,0 @@ -import '../helpers/mock-client' -import '../helpers/mock-indexer' - -import { RediSearchSchema, SchemaFieldTypes } from 'redis' - -import { Client } from '$lib/client' -import { buildRediSearchSchema } from '$lib/indexer' -import { Repository } from '$lib/repository' - -import { simpleSchema, stopWordsOffSchema, customStopWordsSchema } from '../helpers/test-entity-and-schema' - -const bogusSchema: RediSearchSchema = { - foo: { type: SchemaFieldTypes.TAG }, - bar: { type: SchemaFieldTypes.TEXT } -} - -describe("Repository", () => { - - let client: Client - let repository: Repository - - beforeAll(() => { client = new Client() }) - - describe("#createIndex", () => { - describe("with a simple schema", () => { - - beforeEach(() => { repository = new Repository(simpleSchema, client) }) - - describe("and an index that doesn't exist", () => { - beforeEach(async () => { - vi.mocked(client.get).mockResolvedValue(null) - vi.mocked(buildRediSearchSchema).mockReturnValue(bogusSchema) - await repository.createIndex() - }) - - it("asks the client for the index hash", () => - expect(client.get).toHaveBeenCalledWith(simpleSchema.indexHashName)) - - it("asks the index builder to build the index", () => - expect(buildRediSearchSchema).toHaveBeenCalledWith(simpleSchema)) - - it("asks the client to create the index with data from the schema", () => { - const { indexName, dataStructure, schemaName: prefix } = simpleSchema - expect(client.createIndex).toHaveBeenCalledWith(indexName, bogusSchema, { - ON: dataStructure, - PREFIX: `${prefix}:` - }) - }) - - it("asks the client to write the index hash", () => - expect(client.set).toHaveBeenCalledWith(simpleSchema.indexHashName, simpleSchema.indexHash)) - }) - - describe("and an index that exists and is the same", () => { - beforeEach(async () => { - vi.mocked(client.get).mockResolvedValue(simpleSchema.indexHash) - await repository.createIndex() - }) - - it("asks the client for the index hash", () => - expect(client.get).toHaveBeenCalledWith(simpleSchema.indexHashName)) - - it("doesn't ask the client to remove the current index", () => - expect(client.dropIndex).not.toHaveBeenCalled()) - - it("doesn't ask the client to remove the index hash", async () => - expect(client.unlink).not.toHaveBeenCalled()) - - it("doesn't ask the index builder to build the index", () => - expect(buildRediSearchSchema).not.toHaveBeenCalledWith()) - - it("does not ask the client to create the index with data from the schema", () => - expect(client.createIndex).not.toHaveBeenCalled()) - - it("does not asks the client to write the index hash", () => - expect(client.set).not.toHaveBeenCalled()) - }) - - describe("and an index that exists and is different", () => { - beforeEach(async () => { - vi.mocked(client.get).mockResolvedValue('A_MISMATCHED_INDEX_HASH') - vi.mocked(buildRediSearchSchema).mockReturnValue(bogusSchema) - await repository.createIndex() - }) - - it("asks the client for the index hash", () => - expect(client.get).toHaveBeenCalledWith(simpleSchema.indexHashName)) - - it("asks the client to remove the current index", () => - expect(client.dropIndex).toHaveBeenCalledWith(simpleSchema.indexName)) - - it("asks the client to remove the index hash", async () => - expect(client.unlink).toHaveBeenCalledWith(simpleSchema.indexHashName)) - - it("asks the index builder to build the index", () => - expect(buildRediSearchSchema).toHaveBeenCalledWith(simpleSchema)) - - it("asks the client to create a new index with data from the schema", () => { - const { indexName, dataStructure, schemaName: prefix } = simpleSchema - expect(client.createIndex).toHaveBeenCalledWith(indexName, bogusSchema, { - ON: dataStructure, - PREFIX: `${prefix}:` - }) - }) - - it("asks the client to write the index hash", () => - expect(client.set).toHaveBeenCalledWith(simpleSchema.indexHashName, simpleSchema.indexHash)) - }) - }) - - describe("with stop words turned off", () => { - - beforeEach(async () => { - repository = new Repository(stopWordsOffSchema, client) - await repository.createIndex() - }) - - it("asks the client to create the index with data from the schema", () => { - const { indexName, dataStructure, schemaName: prefix, stopWords } = stopWordsOffSchema - expect(client.createIndex).toHaveBeenCalledWith(indexName, bogusSchema, { - ON: dataStructure, - PREFIX: `${prefix}:`, - STOPWORDS: stopWords - }) - }) - }) - - describe("with custom stop words", () => { - - beforeEach(async () => { - repository = new Repository(customStopWordsSchema, client) - await repository.createIndex() - }) - - it("asks the client to create the index with data from the schema", () => { - const { indexName, dataStructure, schemaName: prefix, stopWords } = customStopWordsSchema - expect(client.createIndex).toHaveBeenCalledWith(indexName, bogusSchema, { - ON: dataStructure, - PREFIX: `${prefix}:`, - STOPWORDS: stopWords - }) - }) - }) - }) -}) diff --git a/spec/unit/repository/repository-drop-index.spec.ts b/spec/unit/repository/repository-drop-index.spec.ts deleted file mode 100644 index 80f9163..0000000 --- a/spec/unit/repository/repository-drop-index.spec.ts +++ /dev/null @@ -1,48 +0,0 @@ -import '../helpers/mock-client' -import '../../helpers/custom-matchers' - -import { Client } from '$lib/client' -import { Repository } from '$lib/repository' -import { Schema } from '$lib/schema' - - -const simpleSchema = new Schema("SimpleEntity", {}, { dataStructure: 'HASH' }) - -describe("Repository", () => { - - let client: Client - let repository: Repository - - describe("#dropIndex", () => { - - beforeAll(() => { client = new Client() }) - beforeEach(() => { repository = new Repository(simpleSchema, client) }) - - describe("when the index exists", () => { - beforeEach(async () => await repository.dropIndex()) - - it("asks the client to drop the index", async () => - expect(client.dropIndex).toHaveBeenCalledWith(simpleSchema.indexName)) - - it("asks the client to remove the index hash", async () => - expect(client.unlink).toHaveBeenCalledWith(simpleSchema.indexHashName)) - }) - - describe("when the index doesn't exist", () => { - beforeEach(async () => { - vi.mocked(client.dropIndex).mockRejectedValue(Error("Unknown Index name")) - }) - - it("eats the exception", async () => await repository.dropIndex()) // it doesn't throw an exception - }) - - describe("when dropping the index throws some other Redis exception", () => { - beforeEach(async () => { - vi.mocked(client.dropIndex).mockRejectedValue(Error("Some other error")) - }) - - it("propogates the exception", async () => - expect(async () => await repository.dropIndex()).rejects.toThrowErrorOfType(Error, "Some other error")) - }) - }) -}) diff --git a/spec/unit/repository/repository-expire.spec.ts b/spec/unit/repository/repository-expire.spec.ts deleted file mode 100644 index 9eb8243..0000000 --- a/spec/unit/repository/repository-expire.spec.ts +++ /dev/null @@ -1,30 +0,0 @@ -import '../helpers/mock-client' - -import { Client } from '$lib/client' -import { Repository } from '$lib/repository' -import { Schema } from '$lib/schema' - -const simpleSchema = new Schema("SimpleEntity", {}, { dataStructure: 'HASH' }) - -describe("Repository", () => { - describe("#expire", () => { - - let client: Client - let repository: Repository - - beforeAll(() => { client = new Client() }) - beforeEach(() => { repository = new Repository(simpleSchema, client) }) - - it("expires a single entity", async () => { - await repository.expire('foo', 60) - expect(client.expire).toHaveBeenCalledWith('SimpleEntity:foo', 60) - }) - - it("expires a multiple entities", async () => { - await repository.expire(['foo', 'bar', 'baz'], 60) - expect(client.expire).toHaveBeenNthCalledWith(1, 'SimpleEntity:foo', 60) - expect(client.expire).toHaveBeenNthCalledWith(2, 'SimpleEntity:bar', 60) - expect(client.expire).toHaveBeenNthCalledWith(3, 'SimpleEntity:baz', 60) - }) - }) -}) diff --git a/spec/unit/repository/repository-fetch.spec.ts b/spec/unit/repository/repository-fetch.spec.ts deleted file mode 100644 index beb5f9b..0000000 --- a/spec/unit/repository/repository-fetch.spec.ts +++ /dev/null @@ -1,142 +0,0 @@ -import '../helpers/mock-client' - -import { Client } from '$lib/client' -import { Entity, EntityId, EntityKeyName } from '$lib/entity' -import { Repository } from '$lib/repository' -import { Schema, SchemaDefinition } from '$lib/schema' - -import { A_NUMBER, A_NUMBER_STRING, A_STRING, ANOTHER_STRING, A_THIRD_STRING, ANOTHER_NUMBER_STRING, A_THIRD_NUMBER_STRING, ANOTHER_NUMBER, A_THIRD_NUMBER } from '../../helpers/example-data' - -const aSimpleSchemaDef: SchemaDefinition = { - aString: { type: 'string' }, - aNumber: { type: 'number' }, - aBoolean: { type: 'boolean' } -} - -const simpleHashSchema = new Schema("SimpleEntity", aSimpleSchemaDef, { dataStructure: 'HASH' }) -const simpleJsonSchema = new Schema("SimpleEntity", aSimpleSchemaDef, { dataStructure: 'JSON' }) - -const AN_ENTITY = { [EntityId]: 'foo', [EntityKeyName]: 'SimpleEntity:foo', aString: A_STRING, aNumber: A_NUMBER, aBoolean: true } -const ANOTHER_ENTITY = { [EntityId]: 'bar', [EntityKeyName]: 'SimpleEntity:bar', aString: ANOTHER_STRING, aNumber: ANOTHER_NUMBER, aBoolean: false } -const A_THIRD_ENTITY = { [EntityId]: 'baz', [EntityKeyName]: 'SimpleEntity:baz', aString: A_THIRD_STRING, aNumber: A_THIRD_NUMBER, aBoolean: true } -const AN_EMPTY_ENTITY = { [EntityId]: 'empty', [EntityKeyName]: 'SimpleEntity:empty' } - -const SOME_ENTITY_HASH_DATA = { aString: A_STRING, aNumber: A_NUMBER_STRING, aBoolean: '1' } -const SOME_OTHER_ENTITY_HASH_DATA = { aString: ANOTHER_STRING, aNumber: ANOTHER_NUMBER_STRING, aBoolean: '0' } -const SOME_MORE_ENTITY_HASH_DATA = { aString: A_THIRD_STRING, aNumber: A_THIRD_NUMBER_STRING, aBoolean: '1' } - -const SOME_ENTITY_JSON_DATA = { aString: A_STRING, aNumber: A_NUMBER, aBoolean: true } -const SOME_OTHER_ENTITY_JSON_DATA = { aString: ANOTHER_STRING, aNumber: ANOTHER_NUMBER, aBoolean: false } -const SOME_MORE_ENTITY_JSON_DATA = { aString: A_THIRD_STRING, aNumber: A_THIRD_NUMBER, aBoolean: true } - -describe("Repository", () => { - - let client: Client - let repository: Repository - let entity: Entity, entities: Entity[] - - describe("#fetch", () => { - - beforeAll(() => { client = new Client() }) - - describe("when fetching a single entity from a hash", () => { - beforeEach(async () => { - repository = new Repository(simpleHashSchema, client) - vi.mocked(client.hgetall).mockResolvedValue(SOME_ENTITY_HASH_DATA) - entity = await repository.fetch('foo') - }) - - it("returns the expected entity", () => expect(entity).toEqual(AN_ENTITY)) - }) - - describe("when fetching a empty entity from a hash", () => { - beforeEach(async () => { - repository = new Repository(simpleHashSchema, client) - vi.mocked(client.hgetall).mockResolvedValue({}) - entity = await repository.fetch('empty') - }) - - it("returns the expected entity", () => expect(entity).toEqual(AN_EMPTY_ENTITY)) - }) - - describe("when fetching multiple entities from a hash", () => { - beforeEach(async () => { - repository = new Repository(simpleHashSchema, client) - vi.mocked(client.hgetall) - .mockResolvedValueOnce(SOME_ENTITY_HASH_DATA) - .mockResolvedValueOnce(SOME_OTHER_ENTITY_HASH_DATA) - .mockResolvedValueOnce(SOME_MORE_ENTITY_HASH_DATA) - entities = await repository.fetch(['foo', 'bar', 'baz']) - }) - - it("returns the expected number of entities", () => expect(entities).toHaveLength(3)) - it("returns the expected entities", () => - expect(entities).toEqual(expect.arrayContaining([ AN_ENTITY, ANOTHER_ENTITY, A_THIRD_ENTITY ]))) - }) - - describe("when fetching multiple entities from a hash discretely", () => { - beforeEach(async () => { - repository = new Repository(simpleHashSchema, client) - vi.mocked(client.hgetall) - .mockResolvedValueOnce(SOME_ENTITY_HASH_DATA) - .mockResolvedValueOnce(SOME_OTHER_ENTITY_HASH_DATA) - .mockResolvedValueOnce(SOME_MORE_ENTITY_HASH_DATA) - entities = await repository.fetch('foo', 'bar', 'baz') - }) - - it("returns the expected number of entities", () => expect(entities).toHaveLength(3)) - it("returns the expected entities", () => - expect(entities).toEqual(expect.arrayContaining([ AN_ENTITY, ANOTHER_ENTITY, A_THIRD_ENTITY ]))) - }) - - describe("when fetching a single entity from JSON", () => { - beforeEach(async () => { - repository = new Repository(simpleJsonSchema, client) - vi.mocked(client.jsonget).mockResolvedValue(SOME_ENTITY_JSON_DATA) - entity = await repository.fetch('foo') - }) - - it("returns the expected entity", () => expect(entity).toEqual(AN_ENTITY)) - }) - - describe("when fetching a empty entity from JSON", () => { - beforeEach(async () => { - repository = new Repository(simpleJsonSchema, client) - vi.mocked(client.jsonget).mockResolvedValue(null) - entity = await repository.fetch('empty') - }) - - it("returns the expected entity", () => expect(entity).toEqual(AN_EMPTY_ENTITY)) - }) - - describe("when fetching multiple entities from JSON", () => { - beforeEach(async () => { - repository = new Repository(simpleJsonSchema, client) - vi.mocked(client.jsonget) - .mockResolvedValueOnce(SOME_ENTITY_JSON_DATA) - .mockResolvedValueOnce(SOME_OTHER_ENTITY_JSON_DATA) - .mockResolvedValueOnce(SOME_MORE_ENTITY_JSON_DATA) - entities = await repository.fetch(['foo', 'bar', 'baz']) - }) - - it("returns the expected number of entities", () => expect(entities).toHaveLength(3)) - it("returns the expected entities", () => - expect(entities).toEqual(expect.arrayContaining([ AN_ENTITY, ANOTHER_ENTITY, A_THIRD_ENTITY ]))) - }) - - describe("when fetching multiple entities from JSON discretely", () => { - beforeEach(async () => { - repository = new Repository(simpleJsonSchema, client) - vi.mocked(client.jsonget) - .mockResolvedValueOnce(SOME_ENTITY_JSON_DATA) - .mockResolvedValueOnce(SOME_OTHER_ENTITY_JSON_DATA) - .mockResolvedValueOnce(SOME_MORE_ENTITY_JSON_DATA) - entities = await repository.fetch('foo', 'bar', 'baz') - }) - - it("returns the expected number of entities", () => expect(entities).toHaveLength(3)) - it("returns the expected entities", () => - expect(entities).toEqual(expect.arrayContaining([ AN_ENTITY, ANOTHER_ENTITY, A_THIRD_ENTITY ]))) - }) - }) -}) diff --git a/spec/unit/repository/repository-remove.spec.ts b/spec/unit/repository/repository-remove.spec.ts deleted file mode 100644 index 14f461e..0000000 --- a/spec/unit/repository/repository-remove.spec.ts +++ /dev/null @@ -1,42 +0,0 @@ -import '../helpers/mock-client' - -import { Client } from '$lib/client' -import { Repository } from '$lib/repository' -import { Schema } from '$lib/schema' - -const simpleSchema = new Schema("SimpleEntity", {}, { dataStructure: 'HASH' }) - -describe("Repository", () => { - describe("#remove", () => { - - let client: Client - let repository: Repository - - beforeAll(() => { client = new Client() }) - beforeEach(() => { repository = new Repository(simpleSchema, client) }) - - it("removes no entities", async () => { - await repository.remove() - expect(client.unlink).not.toHaveBeenCalled() - }) - - it("removes a single entity", async () => { - await repository.remove('foo') - expect(client.unlink).toHaveBeenCalledWith('SimpleEntity:foo') - }) - - it("removes multiple entities", async () => { - await repository.remove('foo', 'bar', 'baz') - expect(client.unlink).toHaveBeenCalledWith( - 'SimpleEntity:foo', 'SimpleEntity:bar', 'SimpleEntity:baz' - ) - }) - - it("removes multiple entities discretely", async () => { - await repository.remove(['foo', 'bar', 'baz']) - expect(client.unlink).toHaveBeenCalledWith( - 'SimpleEntity:foo', 'SimpleEntity:bar', 'SimpleEntity:baz' - ) - }) - }) -}) diff --git a/spec/unit/repository/repository-save.spec.ts b/spec/unit/repository/repository-save.spec.ts deleted file mode 100644 index 2c3da76..0000000 --- a/spec/unit/repository/repository-save.spec.ts +++ /dev/null @@ -1,177 +0,0 @@ -import '../helpers/mock-client' - -import { Client } from '$lib/client' -import { Repository } from '$lib/repository' -import { Schema, SchemaDefinition } from '$lib/schema' - -import { A_STRING, A_NUMBER, A_NUMBER_STRING } from '../../helpers/example-data' -import { Entity, EntityId, EntityKeyName } from '$lib/entity' - -const aSimpleSchemaDef: SchemaDefinition = { - aString: { type: 'string' }, - aNumber: { type: 'number' }, - aBoolean: { type: 'boolean' } -} - -const simpleHashSchema = new Schema("SimpleEntity", aSimpleSchemaDef, { dataStructure: 'HASH' }) -const simpleJsonSchema = new Schema("SimpleEntity", aSimpleSchemaDef, { dataStructure: 'JSON' }) - -const EMPTY_ENTITY = {} -const EMPTY_ENTITY_WITH_ID = { [EntityId]: 'foo', [EntityKeyName]: 'key:foo' } -const ENTITY = { aString: A_STRING, aNumber: A_NUMBER, aBoolean: true } -const ENTITY_WITH_ID = { [EntityId]: 'foo', [EntityKeyName]: 'key:foo', aString: A_STRING, aNumber: A_NUMBER, aBoolean: true } - -const ENTITY_HASH_DATA = { aString: A_STRING, aNumber: A_NUMBER_STRING, aBoolean: '1' } -const ENTITY_JSON_DATA = { aString: A_STRING, aNumber: A_NUMBER, aBoolean: true } -const ULID_REGEX = /^[0-9ABCDEFGHJKMNPQRSTVWXYZ]{26}$/ -const KEYNAME_REGEX = /^SimpleEntity:[0-9ABCDEFGHJKMNPQRSTVWXYZ]{26}$/ - - -describe("Repository", () => { - describe("#save", () => { - - let client: Client - let returnedEntity: Entity - - beforeAll(() => { client = new Client() }) - - describe("to a Hash", () => { - let repository: Repository - - beforeAll(() => { repository = new Repository(simpleHashSchema, client) }) - - describe.each([ - [ "when saving an entity without an entityId", ENTITY, ULID_REGEX, KEYNAME_REGEX, false ], - [ "when saving an entity with an entityId", ENTITY_WITH_ID, 'foo', 'SimpleEntity:foo', false ], - [ "when saving an empty entity without an entityId", EMPTY_ENTITY, ULID_REGEX, KEYNAME_REGEX, true ], - [ "when saving an empty entity with an entityId", EMPTY_ENTITY_WITH_ID, 'foo', 'SimpleEntity:foo', true ] - ])('%s', (_, entity, entityIdRegex, keyNameRegex, isEmpty) => { - - beforeEach(async () => { returnedEntity = await repository.save(entity) }) - - it("returns the expected entity", () => expect(returnedEntity).toEqual({ - ...entity, - [EntityId]: expect.stringMatching(entityIdRegex), - [EntityKeyName]: expect.stringMatching(keyNameRegex) - })) - - if (!isEmpty) it("saves the entity data to the key", () => - expect(client.hsetall).toHaveBeenCalledWith( - expect.stringMatching(keyNameRegex), - expect.objectContaining(ENTITY_HASH_DATA))) - - if (isEmpty) it("unlinks the expected key", () => - expect(client.unlink).toHaveBeenCalledWith(expect.stringMatching(keyNameRegex))) - }) - - describe.each([ - [ "when saving an entity without an entityId but with a provided id", 'foo', ENTITY, 'foo', 'SimpleEntity:foo', false ], - [ "when saving an entity with an entityId *and* a provided id", 'bar', ENTITY_WITH_ID, 'bar', 'SimpleEntity:bar', false ], - [ "when saving an empty entity without an entityId but with a provided id", 'foo', EMPTY_ENTITY, 'foo', 'SimpleEntity:foo', true ], - [ "when saving an empty entity with an entityId *and* a provided id", 'bar', EMPTY_ENTITY_WITH_ID, 'bar', 'SimpleEntity:bar', true ], - ])('%s', (_, id, entity, entityIdRegex, keyNameRegex, isEmpty) => { - - beforeEach(async () => { returnedEntity = await repository.save(id, entity) }) - - it("returns the expected entity", () => expect(returnedEntity).toEqual({ - ...entity, - [EntityId]: expect.stringMatching(entityIdRegex), - [EntityKeyName]: expect.stringMatching(keyNameRegex) - })) - - if (!isEmpty) it("saves the entity data to the key", () => - expect(client.hsetall).toHaveBeenCalledWith( - expect.stringMatching(keyNameRegex), - expect.objectContaining(ENTITY_HASH_DATA))) - - if (isEmpty) it("unlinks the expected key", () => - expect(client.unlink).toHaveBeenCalledWith(expect.stringMatching(keyNameRegex))) - }) - }) - - describe("to JSON", () => { - - let repository: Repository - - beforeAll(() => { repository = new Repository(simpleJsonSchema, client) }) - - describe.each([ - [ "when saving an entity without an entityId", ENTITY, ULID_REGEX, KEYNAME_REGEX, false ], - [ "when saving an entity with an entityId", ENTITY_WITH_ID, 'foo', 'SimpleEntity:foo', false ], - [ "when saving an empty entity without an entityId", EMPTY_ENTITY, ULID_REGEX, KEYNAME_REGEX, true ], - [ "when saving an empty entity with an entityId", EMPTY_ENTITY_WITH_ID, 'foo', 'SimpleEntity:foo', true ] - ])('%s', (_, entity, entityIdRegex, keyNameRegex, isEmpty) => { - - beforeEach(async () => { returnedEntity = await repository.save(entity) }) - - describe("the returned entity", () => { - it("has a generated entity id", () => expect(returnedEntity[EntityId]).toMatch(entityIdRegex)) - it("has a keyname based on the entity id", () => expect(returnedEntity[EntityKeyName]).toMatch(keyNameRegex)) - - if (isEmpty) { - it("has populated properties", () => { - expect(returnedEntity.aString).toBeUndefined() - expect(returnedEntity.aNumber).toBeUndefined() - expect(returnedEntity.aBoolean).toBeUndefined() - }) - } else { - it("has populated properties", () => { - expect(returnedEntity.aString).toBe(A_STRING) - expect(returnedEntity.aNumber).toBe(A_NUMBER) - expect(returnedEntity.aBoolean).toBe(true) - }) - } - }) - - if (!isEmpty) it("saves the entity data to the key", () => - expect(client.jsonset).toHaveBeenCalledWith( - expect.stringMatching(keyNameRegex), - expect.objectContaining(ENTITY_JSON_DATA))) - - if (isEmpty) it("save the empty data to the key", () => - expect(client.jsonset).toHaveBeenCalledWith( - expect.stringMatching(keyNameRegex), - expect.objectContaining({}))) - }) - - describe.each([ - [ "when saving an entity without an entityId but with a provided id", 'foo', ENTITY, 'foo', 'SimpleEntity:foo', false ], - [ "when saving an entity with an entityId *and* a provided id", 'bar', ENTITY_WITH_ID, 'bar', 'SimpleEntity:bar', false ], - [ "when saving an empty entity without an entityId but with a provided id", 'foo', EMPTY_ENTITY, 'foo', 'SimpleEntity:foo', true ], - [ "when saving an empty entity with an entityId *and* a provided id", 'bar', EMPTY_ENTITY_WITH_ID, 'bar', 'SimpleEntity:bar', true ], - ])('%s', (_, id, entity, entityIdRegex, keyNameRegex, isEmpty) => { - - beforeEach(async () => { returnedEntity = await repository.save(id, entity) }) - - describe("the returned entity", () => { - it("has a generated entity id", () => expect(returnedEntity[EntityId]).toMatch(entityIdRegex)) - it("has a keyname based on the entity id", () => expect(returnedEntity[EntityKeyName]).toMatch(keyNameRegex)) - - if (isEmpty) { - it("has populated properties", () => { - expect(returnedEntity.aString).toBeUndefined() - expect(returnedEntity.aNumber).toBeUndefined() - expect(returnedEntity.aBoolean).toBeUndefined() - }) - } else { - it("has populated properties", () => { - expect(returnedEntity.aString).toBe(A_STRING) - expect(returnedEntity.aNumber).toBe(A_NUMBER) - expect(returnedEntity.aBoolean).toBe(true) - }) - } - }) - - if (!isEmpty) it("saves the entity data to the key", () => - expect(client.jsonset).toHaveBeenCalledWith( - expect.stringMatching(keyNameRegex), - expect.objectContaining(ENTITY_JSON_DATA))) - - if (isEmpty) it("save the empty data to the key", () => - expect(client.jsonset).toHaveBeenCalledWith( - expect.stringMatching(keyNameRegex), - expect.objectContaining({}))) - }) - }) - }) -}) diff --git a/spec/unit/repository/repository-search.spec.ts b/spec/unit/repository/repository-search.spec.ts deleted file mode 100644 index f2b0229..0000000 --- a/spec/unit/repository/repository-search.spec.ts +++ /dev/null @@ -1,67 +0,0 @@ -import '../helpers/mock-client' - -import { Client } from '$lib/client' -import { AbstractSearch, Search, RawSearch } from '$lib/search' -import { Repository } from '$lib/repository' - -import { simpleSchema } from '../helpers/test-entity-and-schema' - -describe("Repository", () => { - - let client: Client - let repository: Repository - let search: AbstractSearch - - beforeEach(() => { - client = new Client() - repository = new Repository(simpleSchema, client) - }) - - describe("#searchRaw", () => { - - beforeEach(async () => { - search = repository.searchRaw("NOT A VALID QUERY BUT HEY WHATEVER") - }) - - it("returns a RawSearch instance", () => expect(search).toBeInstanceOf(RawSearch)) - - describe("the RawSearch instance", () => { - it("has the provided schema", () => { - // @ts-ignore: peek inside since I can't mock the constructor - expect(search.schema).toBe(simpleSchema) - }) - - it("has the provided client", () => { - // @ts-ignore: peek inside since I can't mock the constructor - expect(search.client).toBe(client) - }) - - it("has the provided query", () => { - // @ts-ignore: peek inside since I can't mock the constructor - expect(search.rawQuery).toBe("NOT A VALID QUERY BUT HEY WHATEVER") - }) - }) - }) - - describe("#search", () => { - - beforeEach(async () => { - repository = new Repository(simpleSchema, client); - search = repository.search(); - }) - - it("returns a Search instance", () => expect(search).toBeInstanceOf(Search)) - - describe("the Search instance", () => { - it("has the provided schema", () => { - // @ts-ignore: peek inside since I can't mock the constructor - expect(search.schema).toBe(simpleSchema) - }) - - it("has the provided client", () => { - // @ts-ignore: peek inside since I can't mock the constructor - expect(search.client).toBe(client) - }) - }) - }) -}) diff --git a/spec/unit/schema/field.spec.ts b/spec/unit/schema/field.spec.ts deleted file mode 100644 index 8fd7bb1..0000000 --- a/spec/unit/schema/field.spec.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { Field } from '$lib/schema' - -describe("Field", () => { - - let field: Field - - const expectedName = 'foo' - const expectedType = 'string' - - describe("that is unconfigured", () => { - - beforeEach(() => { field = new Field('foo', { type: 'string' }) }) - - it("has the expected name", () => expect(field.name).toBe(expectedName)) - it("has the expected type", () => expect(field.type).toBe(expectedType)) - it("has the default sortable property", () => expect(field.sortable).toBe(false)) - it("has the default caseSensitive property", () => expect(field.caseSensitive).toBe(false)) - it("has the default indexed property", () => expect(field.indexed).toBe(true)) - it("has the default stemming property", () => expect(field.stemming).toBe(true)) - it("has the default normalized property", () => expect(field.normalized).toBe(true)) - it("has the default weight property of null", () => expect(field.weight).toBeNull()) - it("has the default phonetic match property of null", () => expect(field.matcher).toBeNull()) - }) - - describe.each([ - - /* just the type */ - ['configured as a boolean', { type: 'boolean' }, { expectedField: 'foo', expectedPath: '$.foo' }], - ['configured as a number', { type: 'number' }, { expectedField: 'foo', expectedPath: '$.foo' }], - ['configured as a date', { type: 'date' }, { expectedField: 'foo', expectedPath: '$.foo' }], - ['configured as a point', { type: 'point' }, { expectedField: 'foo', expectedPath: '$.foo' }], - ['configured as a text', { type: 'text' }, { expectedField: 'foo', expectedPath: '$.foo' }], - ['configured as a string', { type: 'string' }, { expectedField: 'foo', expectedPath: '$.foo' }], - ['configured as a string[]', { type: 'string[]' }, { expectedField: 'foo', expectedPath: '$.foo[*]' }], - - /* aliased string */ - ['configured as a string with an alias', { type: 'string', alias: 'bar' }, { expectedField: 'bar', expectedPath: '$.bar' }], - ['configured as a string with a field', { type: 'string', field: 'bar' }, { expectedField: 'bar', expectedPath: '$.foo' }], - ['configured as a string with a field and an alias', { type: 'string', alias: 'bar', field: 'baz' }, { expectedField: 'baz', expectedPath: '$.bar' }], - ['configured as a string with a path', { type: 'string', path: '$.bar.baz' }, { expectedField: 'foo', expectedPath: '$.bar.baz' }], - ['configured as a string with a path and an alias', { type: 'string', alias: 'bar', path: '$.baz.qux' }, { expectedField: 'bar', expectedPath: '$.baz.qux' }], - - /* aliased string[] */ - ['configured as a string[] with an alias', { type: 'string[]', alias: 'bar' }, { expectedField: 'bar', expectedPath: '$.bar[*]' }], - ['configured as a string[] with a field', { type: 'string[]', field: 'bar' }, { expectedField: 'bar', expectedPath: '$.foo[*]' }], - ['configured as a string[] with a field and an alias', { type: 'string[]', field: 'bar' }, { expectedField: 'bar', expectedPath: '$.foo[*]' }], - ['configured as a string[] with a path', { type: 'string[]', path: '$.bar.baz' }, { expectedField: 'foo', expectedPath: '$.bar.baz' }], - ['configured as a string[] with a path and an alias', { type: 'string[]', alias: 'bar', path: '$.baz.qux' }, { expectedField: 'bar', expectedPath: '$.baz.qux' }] - - ])('%s', (_, fieldDef: any, { expectedField, expectedPath } ) => { - - beforeEach(() => { field = new Field('foo', fieldDef) }) - - it("has the expected name", () => expect(field.name).toBe(expectedName)) - it("has the expected type", () => expect(field.type).toBe(fieldDef.type)) - it("has the name as the Hash field", () => expect(field.hashField).toBe(expectedField)) - it("has the name as a root JSON path", () => expect(field.jsonPath).toBe(expectedPath)) - }) - - describe.each([ - ['sortable', true], - ['sortable', false], - ['caseSensitive', true], - ['caseSensitive', false], - ['indexed', true], - ['indexed', false], - ['stemming', true], - ['stemming', false], - ['normalized', true], - ['normalized', false], - ['weight', 2.5], - ['matcher', 'dm:en'], - ])('that has %s defined', (propertyName, value) => { - describe(`as ${value}`, () => { - - beforeEach(() => { - const fieldDef: any = {} - fieldDef.type = 'string' - fieldDef[propertyName] = value - field = new Field('foo', fieldDef) - }) - - it(`has the expected ${propertyName} property`, () => { - expect((field as any)[propertyName]).toBe(value) - }) - }) - }) -}) diff --git a/spec/unit/schema/schema-index-hash.spec.ts b/spec/unit/schema/schema-index-hash.spec.ts deleted file mode 100644 index 53f737d..0000000 --- a/spec/unit/schema/schema-index-hash.spec.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { Schema, SchemaDefinition, SchemaOptions } from '$lib/schema' - -const EMPTY_SCHEMA_DEF: SchemaDefinition = {} -const WELL_POPULATED_SCHEMA_DEF: SchemaDefinition = { - aString: { type: 'string' }, anotherString: { type: 'string' }, - someText: { type: 'text' }, someOtherText: { type: 'text' }, - aNumber: { type: 'number' }, anotherNumber: { type: 'number' }, - aBoolean: { type: 'boolean' }, anotherBoolean: { type: 'boolean' }, - aPoint: { type: 'point' }, anotherPoint: { type: 'point' }, - aDate: { type: 'date' }, anotherDate: { type: 'date' }, - someStrings: { type: 'string[]' }, someOtherStrings: { type: 'string[]' } -} - -const EMPTY_HASH = "9UJTUMAzgvhnE/cOJXT1D3KPGYg=" -const WELL_POPULATED_HASH = "F+GgQDhzmXhvTNhQczPZtCIJ0BA=" - -describe("Schema", () => { - describe("#indexHash", () => { - - describe.each([ - ["that is given a well populated schema", { - schemaDef: WELL_POPULATED_SCHEMA_DEF, - options: {} as SchemaOptions, - expectedHash: WELL_POPULATED_HASH - }], - ["that is given an empty schema", { - schemaDef: EMPTY_SCHEMA_DEF, - options: {} as SchemaOptions, - expectedHash: EMPTY_HASH - }], - ["that is given the default data strucutre of JSON", { - schemaDef: EMPTY_SCHEMA_DEF, - options: { dataStructure: 'JSON' } as SchemaOptions, - expectedHash: EMPTY_HASH - }], - ["that overrides the data structure to be HASH", { - schemaDef: EMPTY_SCHEMA_DEF, - options: { dataStructure: 'HASH' } as SchemaOptions, - expectedHash: "nd4P5YFFLxYr/3glJ6Thvlk+0tg=" - }], - ["that overrides the index name", { - schemaDef: EMPTY_SCHEMA_DEF, - options: { indexName: 'test-index' } as SchemaOptions, - expectedHash: "DgFE5XI/doj0oQNTZQ5yqPHtb6M=" - }], - ["that overrides the index hash name", { - schemaDef: EMPTY_SCHEMA_DEF, - options: { indexHashName: 'test-index-hash' } as SchemaOptions, - expectedHash: "p1OuQrsovszDdUD5pnbxTT5sbpI=" - }], - ["that overrides the id generation strategy", { - schemaDef: EMPTY_SCHEMA_DEF, - options: { idStrategy: async () => '1' } as SchemaOptions, - expectedHash: EMPTY_HASH - }], - ["that disables stop words", { - schemaDef: EMPTY_SCHEMA_DEF, - options: { useStopWords: 'OFF' } as SchemaOptions, - expectedHash: "W7+Ri6W8CIo8GUkRY+uabQgpNVA=" - }], - ["that uses default stop words", { - schemaDef: EMPTY_SCHEMA_DEF, - options: { useStopWords: 'DEFAULT' } as SchemaOptions, - expectedHash: EMPTY_HASH - }], - ["that uses custom stop words", { - schemaDef: EMPTY_SCHEMA_DEF, - options: { useStopWords: 'CUSTOM' } as SchemaOptions, - expectedHash: "nEC4s/DEz7EvTApCOKzQlXFTgSA=" - }], - ["that sets custom stop words", { - schemaDef: EMPTY_SCHEMA_DEF, - options: { stopWords: ['foo', 'bar', 'baz'] } as SchemaOptions, - expectedHash: "odwqZJal1kQTrLjIqu79U4ZHtDs=" - }] - ])("%s", (_, data) => { - it("generates the expected hash", () => { - const schema = new Schema('TestEntity', data.schemaDef, data.options) - expect(schema.indexHash).toBe(data.expectedHash) - }) - }) - }) -}) diff --git a/spec/unit/schema/schema.spec.ts b/spec/unit/schema/schema.spec.ts deleted file mode 100644 index 4338038..0000000 --- a/spec/unit/schema/schema.spec.ts +++ /dev/null @@ -1,159 +0,0 @@ -import { InvalidSchema } from '$lib/error' -import { Schema } from '$lib/schema' - -import '../../helpers/custom-matchers' - -describe("Schema", () => { - - let schema: Schema - - - describe("that is empty", () => { - beforeEach(() => { - schema = new Schema('TestEntity', {}) - }) - - it("provides the default data structure", () => expect(schema.dataStructure).toBe("JSON")) - it("has the keyspace prefix from the constructor", () => expect(schema.schemaName).toBe("TestEntity")) - it("generates the index name from the entity constructor name", () => expect(schema.indexName).toBe("TestEntity:index")) - it("generates the index hash name from the entity constructor name", () => expect(schema.indexHashName).toBe("TestEntity:index:hash")) - it("generates default Redis IDs", () => expect(schema.generateId()).resolves.toMatch(/^[0-9ABCDEFGHJKMNPQRSTVWXYZ]{26}$/)) - - it("provides the default stop word settings", () => { - expect(schema.useStopWords).toBe('DEFAULT') - expect(schema.stopWords).toEqual([]) - }) - - it("has no fields", () => expect(schema.fields).toHaveLength(0)) - it("returns null when getting a field by name", () => expect(schema.fieldByName('aBoolean')).toBe(null)) - }) - - describe("that defines fields", () => { - beforeEach(() => { - schema = new Schema('TestEntity', { - aBoolean: { type: 'boolean' }, - aNumber: { type: 'number' }, - aString: { type: 'string' } - }) - }) - - it("has all three fields", () => expect(schema.fields).toHaveLength(3)) - - it("has fields with the expected names and definitions", () => { - expect(schema.fields.map(field => { - const { name, type } = field - return { name, type } - })).toEqual(expect.arrayContaining([ - { name: 'aBoolean', type: 'boolean' }, - { name: 'aNumber', type: 'number' }, - { name: 'aString', type: 'string' } - ])) - }) - - it("returns a field by name", () => { - const field = schema.fieldByName('aBoolean') - expect(field).toBeDefined() - expect(field!.name).toBe('aBoolean') - expect(field!.type).toBe('boolean') - }) - }) - - describe("that overrides the data structure to be JSON", () => { - beforeEach(() => { - schema = new Schema('TestEntity', {}, { dataStructure: 'JSON' }) - }) - it("provides a JSON data structure", () => expect(schema.dataStructure).toBe("JSON")) - }) - - describe("that overrides the data structure to be HASH", () => { - beforeEach(() => { - schema = new Schema('TestEntity', {}, { dataStructure: 'HASH' }) - }) - it("provides a HASH data structure", () => expect(schema.dataStructure).toBe("HASH")) - }) - - describe("that overrides the index name", () => { - beforeEach(() => { - schema = new Schema('TestEntity', {}, { indexName: 'test-index' }) - }) - it("generates the index name from the configured index name, ignoring the prefix", () => expect(schema.indexName).toBe("test-index")) - }) - - describe("that overrides the index hash name", () => { - beforeEach(() => { - schema = new Schema('TestEntity', {}, { indexHashName: 'test-index-hash' }) - }) - it("generates the index hash name from the configured index hash name, ignoring the prefix", () => expect(schema.indexHashName).toBe("test-index-hash")) - }) - - describe("that overrides the id generation strategy", () => { - beforeEach(() => { - schema = new Schema('TestEntity', {}, { idStrategy: async () => '1' }) - }) - it("generates Redis IDs from the strategy", () => expect(schema.generateId()).resolves.toBe('1')) - }) - - describe("that disables stop words", () => { - beforeEach(() => { - schema = new Schema('TestEntity', {}, { useStopWords: 'OFF' }) - }) - it("provides the stop words option", () => expect(schema.useStopWords).toBe('OFF')) - }) - - describe("that uses default stop words", () => { - beforeEach(() => { - schema = new Schema('TestEntity', {}, { useStopWords: 'DEFAULT' }) - }) - it("provides the stop words option", () => expect(schema.useStopWords).toBe('DEFAULT')) - }) - - describe("that uses custom stop words", () => { - beforeEach(() => { - schema = new Schema('TestEntity', {}, { useStopWords: 'CUSTOM' }) - }) - it("provides the stop words option", () => expect(schema.useStopWords).toBe('CUSTOM')) - }) - - describe("that sets custom stop words", () => { - beforeEach(() => { - schema = new Schema('TestEntity', {}, { stopWords: ['foo', 'bar', 'baz'] }) - }) - it("provides the custom stop words", () => expect(schema.stopWords).toEqual(['foo', 'bar', 'baz'])) - }) - - describe("that is misconfigured", () => { - it("throws an exception when keyspace prefix is empty", () => - expect(() => new Schema('', {})).toThrowErrorOfType(InvalidSchema, "Schema name must be a non-empty string.")) - - it("throws an exception when the type is missing on a field definition", () => - // @ts-ignore: JavaScript test - expect(() => new Schema('TestEntity', { aField: {} })) - .toThrowErrorOfType(InvalidSchema, "The field 'aField' is configured with a type of 'undefined'. Valid types include 'boolean', 'date', 'number', 'point', 'string', 'string[]', and 'text'.")) - - it("throws an exception when the type is invalid on a field definition", () => - // @ts-ignore: JavaScript test - expect(() => new Schema('TestEntity', { aField: { type: 'foo' } })) - .toThrowErrorOfType(InvalidSchema, "The field 'aField' is configured with a type of 'foo'. Valid types include 'boolean', 'date', 'number', 'point', 'string', 'string[]', and 'text'.")) - - it("throws an exception when the data structure is invalid", () => { - // @ts-ignore: JavaScript test - expect(() => new Schema('TestEntity', {}, { dataStructure: 'FOO' })) - .toThrowErrorOfType(InvalidSchema, "'FOO' in an invalid data structure. Valid data structures are 'HASH' and 'JSON'.") - }) - - it("throws an exception when use stop words are invalid", () => { - // @ts-ignore: JavaScript test - expect(() => new Schema('TestEntity', {}, { useStopWords: 'FOO' })) - .toThrowErrorOfType(InvalidSchema, "'FOO' in an invalid value for stop words. Valid values are 'OFF', 'DEFAULT', and 'CUSTOM'.") - }) - - it("throws an exception when index name is empty", () => - expect(() => new Schema('TestEntity', {}, { indexName: '' })) - .toThrowErrorOfType(InvalidSchema, "Index name must be a non-empty string.")) - - it("throws an exception when ID strategy is not a function", () => - // @ts-ignore: JavaScript test - expect(() => new Schema('TestEntity', {}, { idStrategy: 'NOT A FUNCTION' })) - .toThrowErrorOfType(InvalidSchema, "ID strategy must be a function that takes no arguments and returns a string.")) - }) -}) diff --git a/spec/unit/search/raw-search-query.spec.ts b/spec/unit/search/raw-search-query.spec.ts deleted file mode 100644 index dacf48e..0000000 --- a/spec/unit/search/raw-search-query.spec.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { Client } from "$lib/client" -import { RawSearch } from "$lib/search" - -import { simpleHashSchema } from "../helpers/test-entity-and-schema" - - -describe("RawSearch", () => { - describe("#query", () => { - - let client: Client - let search: RawSearch - - beforeAll(() => { - client = new Client() - }) - - describe("when constructed with the default query", () => { - beforeEach(() => { - search = new RawSearch(simpleHashSchema, client) - }) - - it("generates the default query", () => { - expect(search.query).toBe("*") - }) - }) - - describe("when constructed with a specified query", () => { - beforeEach(() => { - search = new RawSearch(simpleHashSchema, client, "SOME BOGUS QUERY") - }) - - it("generates the specific query", () => { - expect(search.query).toBe("SOME BOGUS QUERY") - }) - }) - }) -}) diff --git a/spec/unit/search/search-by-boolean.spec.ts b/spec/unit/search/search-by-boolean.spec.ts deleted file mode 100644 index fde1fb8..0000000 --- a/spec/unit/search/search-by-boolean.spec.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { Client } from "$lib/client" -import { Search, WhereField } from "$lib/search" - -import { simpleHashSchema, simpleJsonSchema } from "../helpers/test-entity-and-schema" - - -describe("Search", () => { - describe("#query", () => { - - let client: Client - - const A_TRUE_HASH_QUERY = "(@aBoolean:{1})" - const A_FALSE_HASH_QUERY = "(@aBoolean:{0})" - const A_NEGATED_TRUE_HASH_QUERY = "(-@aBoolean:{1})" - const A_NEGATED_FALSE_HASH_QUERY = "(-@aBoolean:{0})" - - const A_TRUE_JSON_QUERY = "(@aBoolean:{true})" - const A_FALSE_JSON_QUERY = "(@aBoolean:{false})" - const A_NEGATED_TRUE_JSON_QUERY = "(-@aBoolean:{true})" - const A_NEGATED_FALSE_JSON_QUERY = "(-@aBoolean:{false})" - - beforeAll(() => { - client = new Client() - }) - - describe("when generating a query with a boolean for a hash", () => { - - let search: Search - let where: WhereField - - beforeEach(() => { - search = new Search(simpleHashSchema, client) - where = search.where('aBoolean') - }) - - type BooleanChecker = (search: Search) => void - const expectToBeTrueQuery: BooleanChecker = search => expect(search.query).toBe(A_TRUE_HASH_QUERY) - const expectToBeFalseQuery: BooleanChecker = search => expect(search.query).toBe(A_FALSE_HASH_QUERY) - const expectToBeNegatedTrueQuery: BooleanChecker = search => expect(search.query).toBe(A_NEGATED_TRUE_HASH_QUERY) - const expectToBeNegatedFalseQuery: BooleanChecker = search => expect(search.query).toBe(A_NEGATED_FALSE_HASH_QUERY) - - it("generates a query with .eq", () => expectToBeTrueQuery(where.eq(true))) - it("generates a query with .not.eq", () => expectToBeNegatedTrueQuery(where.not.eq(true))) - it("generates a query with .equals", () => expectToBeTrueQuery(where.equals(true))) - it("generates a query with .does.equal", () => expectToBeTrueQuery(where.does.equal(true))) - it("generates a query with .does.not.equal", () => expectToBeNegatedTrueQuery(where.does.not.equal(true))) - it("generates a query with .is.equalTo", () => expectToBeTrueQuery(where.is.equalTo(true))) - it("generates a query with .is.not.equalTo", () => expectToBeNegatedTrueQuery(where.is.not.equalTo(true))) - - it("generates a query with .true", () => expectToBeTrueQuery(where.true())) - it("generates a query with .is.true", () => expectToBeTrueQuery(where.is.true())) - it("generates a query with .is.not.true", () => expectToBeNegatedTrueQuery(where.is.not.true())) - - it("generates a query with .false", () => expectToBeFalseQuery(where.false())) - it("generates a query with .is.false", () => expectToBeFalseQuery(where.is.false())) - it("generates a query with .is.not.false", () => expectToBeNegatedFalseQuery(where.is.not.false())) - }) - - describe("when generating a query with a boolean for JSON", () => { - - let search: Search - let where: WhereField - - beforeEach(() => { - search = new Search(simpleJsonSchema, client) - where = search.where('aBoolean') - }) - - type BooleanChecker = (search: Search) => void - const expectToBeTrueQuery: BooleanChecker = search => expect(search.query).toBe(A_TRUE_JSON_QUERY) - const expectToBeFalseQuery: BooleanChecker = search => expect(search.query).toBe(A_FALSE_JSON_QUERY) - const expectToBeNegatedTrueQuery: BooleanChecker = search => expect(search.query).toBe(A_NEGATED_TRUE_JSON_QUERY) - const expectToBeNegatedFalseQuery: BooleanChecker = search => expect(search.query).toBe(A_NEGATED_FALSE_JSON_QUERY) - - it("generates a query with .eq", () => expectToBeTrueQuery(where.eq(true))) - it("generates a query with .not.eq", () => expectToBeNegatedTrueQuery(where.not.eq(true))) - it("generates a query with .equals", () => expectToBeTrueQuery(where.equals(true))) - it("generates a query with .does.equal", () => expectToBeTrueQuery(where.does.equal(true))) - it("generates a query with .does.not.equal", () => expectToBeNegatedTrueQuery(where.does.not.equal(true))) - it("generates a query with .is.equalTo", () => expectToBeTrueQuery(where.is.equalTo(true))) - it("generates a query with .is.not.equalTo", () => expectToBeNegatedTrueQuery(where.is.not.equalTo(true))) - - it("generates a query with .true", () => expectToBeTrueQuery(where.true())) - it("generates a query with .is.true", () => expectToBeTrueQuery(where.is.true())) - it("generates a query with .is.not.true", () => expectToBeNegatedTrueQuery(where.is.not.true())) - - it("generates a query with .false", () => expectToBeFalseQuery(where.false())) - it("generates a query with .is.false", () => expectToBeFalseQuery(where.is.false())) - it("generates a query with .is.not.false", () => expectToBeNegatedFalseQuery(where.is.not.false())) - }) - }) -}) diff --git a/spec/unit/search/search-by-date.spec.ts b/spec/unit/search/search-by-date.spec.ts deleted file mode 100644 index 7e226f8..0000000 --- a/spec/unit/search/search-by-date.spec.ts +++ /dev/null @@ -1,120 +0,0 @@ -import { Client } from "$lib/client" -import { Search, WhereField } from "$lib/search" - -import { A_DATE, A_DATE_EPOCH, ANOTHER_DATE, ANOTHER_DATE_EPOCH, A_DATE_ISO } from '../../helpers/example-data' -import { simpleSchema } from "../helpers/test-entity-and-schema" - - -describe("Search", () => { - describe("#query", () => { - - let client: Client - let search: Search - let where: WhereField - - const AN_EQUAL_QUERY = `(@aDate:[${A_DATE_EPOCH} ${A_DATE_EPOCH}])` - const A_NEGATED_EQUAL_QUERY = `(-@aDate:[${A_DATE_EPOCH} ${A_DATE_EPOCH}])` - const A_GT_QUERY = `(@aDate:[(${A_DATE_EPOCH} +inf])` - const A_NEGATED_GT_QUERY = `(-@aDate:[(${A_DATE_EPOCH} +inf])` - const A_GTE_QUERY = `(@aDate:[${A_DATE_EPOCH} +inf])` - const A_NEGATED_GTE_QUERY = `(-@aDate:[${A_DATE_EPOCH} +inf])` - const AN_LT_QUERY = `(@aDate:[-inf (${A_DATE_EPOCH}])` - const A_NEGATED_LT_QUERY = `(-@aDate:[-inf (${A_DATE_EPOCH}])` - const AN_LTE_QUERY = `(@aDate:[-inf ${A_DATE_EPOCH}])` - const A_NEGATED_LTE_QUERY = `(-@aDate:[-inf ${A_DATE_EPOCH}])` - const A_BETWEEN_QUERY = `(@aDate:[${ANOTHER_DATE_EPOCH} ${A_DATE_EPOCH}])` - const A_NEGATED_BETWEEN_QUERY = `(-@aDate:[${ANOTHER_DATE_EPOCH} ${A_DATE_EPOCH}])` - - type RangeChecker = (search: Search) => void - const expectToBeEqualQuery: RangeChecker = search => expect(search.query).toBe(AN_EQUAL_QUERY) - const expectToBeNegatedEqualQuery: RangeChecker = search => expect(search.query).toBe(A_NEGATED_EQUAL_QUERY) - const expectToBeGTQuery: RangeChecker = search => expect(search.query).toBe(A_GT_QUERY) - const expectToBeNegatedGTQuery: RangeChecker = search => expect(search.query).toBe(A_NEGATED_GT_QUERY) - const expectToBeGTEQuery: RangeChecker = search => expect(search.query).toBe(A_GTE_QUERY) - const expectToBeNegatedGTEQuery: RangeChecker = search => expect(search.query).toBe(A_NEGATED_GTE_QUERY) - const expectToBeLTQuery: RangeChecker = search => expect(search.query).toBe(AN_LT_QUERY) - const expectToBeNegatedLTQuery: RangeChecker = search => expect(search.query).toBe(A_NEGATED_LT_QUERY) - const expectToBeLTEQuery: RangeChecker = search => expect(search.query).toBe(AN_LTE_QUERY) - const expectToBeNegatedLTEQuery: RangeChecker = search => expect(search.query).toBe(A_NEGATED_LTE_QUERY) - const expectToBeBetweenQuery: RangeChecker = search => expect(search.query).toBe(A_BETWEEN_QUERY) - const expectToBeNegatedBetweenQuery: RangeChecker = search => expect(search.query).toBe(A_NEGATED_BETWEEN_QUERY) - - beforeAll(() => { - client = new Client() - }) - - beforeEach(() => { - search = new Search(simpleSchema, client) - where = search.where('aDate') - }) - - describe.each([ - [ "when generating a query with a date as a JavaScript Date type", A_DATE ], - [ "when generating a query with a date as a unix timestamp", A_DATE_EPOCH ], - [ "when generating a query with a date as an ISO-8601 date", A_DATE_ISO ] - ])("%s", (_, aDate: Date | number | string) => { - - it("generates a query with .eq", () => expectToBeEqualQuery(where.eq(aDate))) - it("generates a query with .not.eq", () => expectToBeNegatedEqualQuery(where.not.eq(aDate))) - it("generates a query with .equals", () => expectToBeEqualQuery(where.equals(aDate))) - it("generates a query with .does.equal", () => expectToBeEqualQuery(where.does.equal(aDate))) - it("generates a query with .does.not.equal", () => expectToBeNegatedEqualQuery(where.does.not.equal(aDate))) - it("generates a query with .is.equalTo", () => expectToBeEqualQuery(where.is.equalTo(aDate))) - it("generates a query with .is.not.equalTo", () => expectToBeNegatedEqualQuery(where.is.not.equalTo(aDate))) - - it("generates a query with .gt", () => expectToBeGTQuery(where.gt(aDate))) - it("generates a query with .not.gt", () => expectToBeNegatedGTQuery(where.not.gt(aDate))) - it("generates a query with .greaterThan", () => expectToBeGTQuery(where.greaterThan(aDate))) - it("generates a query with .is.greaterThan", () => expectToBeGTQuery(where.is.greaterThan(aDate))) - it("generates a query with .is.not.greaterThan", () => expectToBeNegatedGTQuery(where.is.not.greaterThan(aDate))) - - it("generates a query with .gte", () => expectToBeGTEQuery(where.gte(aDate))) - it("generates a query with .not.gte", () => expectToBeNegatedGTEQuery(where.not.gte(aDate))) - it("generates a query with .greaterThanOrEqualTo", () => expectToBeGTEQuery(where.greaterThanOrEqualTo(aDate))) - it("generates a query with .is.greaterThanOrEqualTo", () => expectToBeGTEQuery(where.is.greaterThanOrEqualTo(aDate))) - it("generates a query with .is.not.greaterThanOrEqualTo", () => expectToBeNegatedGTEQuery(where.is.not.greaterThanOrEqualTo(aDate))) - - it("generates a query with .lt", () => expectToBeLTQuery(where.lt(aDate))) - it("generates a query with .not.lt", () => expectToBeNegatedLTQuery(where.not.lt(aDate))) - it("generates a query with .lessThan", () => expectToBeLTQuery(where.lessThan(aDate))) - it("generates a query with .is.lessThan", () => expectToBeLTQuery(where.is.lessThan(aDate))) - it("generates a query with .is.not.lessThan", () => expectToBeNegatedLTQuery(where.is.not.lessThan(aDate))) - - it("generates a query with .lte", () => expectToBeLTEQuery(where.lte(aDate))) - it("generates a query with .not.lte", () => expectToBeNegatedLTEQuery(where.not.lte(aDate))) - it("generates a query with .lessThanOrEqualTo", () => expectToBeLTEQuery(where.lessThanOrEqualTo(aDate))) - it("generates a query with .is.lessThanOrEqualTo", () => expectToBeLTEQuery(where.is.lessThanOrEqualTo(aDate))) - it("generates a query with .is.not.lessThanOrEqualTo", () => expectToBeNegatedLTEQuery(where.is.not.lessThanOrEqualTo(aDate))) - - it("generates a query with .on", () => expectToBeEqualQuery(where.on(aDate))) - it("generates a query with .not.on", () => expectToBeNegatedEqualQuery(where.not.on(aDate))) - it("generates a query with .is.on", () => expectToBeEqualQuery(where.is.on(aDate))) - it("generates a query with .is.not.on", () => expectToBeNegatedEqualQuery(where.is.not.on(aDate))) - - it("generates a query with .before", () => expectToBeLTQuery(where.before(aDate))) - it("generates a query with .not.before", () => expectToBeNegatedLTQuery(where.not.before(aDate))) - it("generates a query with .is.before", () => expectToBeLTQuery(where.is.before(aDate))) - it("generates a query with .is.not.before", () => expectToBeNegatedLTQuery(where.is.not.before(aDate))) - - it("generates a query with .onOrBefore", () => expectToBeLTEQuery(where.onOrBefore(aDate))) - it("generates a query with .not.onOrBefore", () => expectToBeNegatedLTEQuery(where.not.onOrBefore(aDate))) - it("generates a query with .is.onOrBefore", () => expectToBeLTEQuery(where.is.onOrBefore(aDate))) - it("generates a query with .is.not.onOrBefore", () => expectToBeNegatedLTEQuery(where.is.not.onOrBefore(aDate))) - - it("generates a query with .after", () => expectToBeGTQuery(where.after(aDate))) - it("generates a query with .not.after", () => expectToBeNegatedGTQuery(where.not.after(aDate))) - it("generates a query with .is.after", () => expectToBeGTQuery(where.is.after(aDate))) - it("generates a query with .is.not.after", () => expectToBeNegatedGTQuery(where.is.not.after(aDate))) - - it("generates a query with .onOrAfter", () => expectToBeGTEQuery(where.onOrAfter(aDate))) - it("generates a query with .not.onOrAfter", () => expectToBeNegatedGTEQuery(where.not.onOrAfter(aDate))) - it("generates a query with .is.onOrAfter", () => expectToBeGTEQuery(where.is.onOrAfter(aDate))) - it("generates a query with .is.not.onOrAfter", () => expectToBeNegatedGTEQuery(where.is.not.onOrAfter(aDate))) - - it("generates a query with .between", () => expectToBeBetweenQuery(where.between(ANOTHER_DATE, aDate))) - it("generates a query with .not.between", () => expectToBeNegatedBetweenQuery(where.not.between(ANOTHER_DATE, aDate))) - it("generates a query with .is.between", () => expectToBeBetweenQuery(where.is.between(ANOTHER_DATE, aDate))) - it("generates a query with .is.not.between", () => expectToBeNegatedBetweenQuery(where.is.not.between(ANOTHER_DATE, aDate))) - }) - }) -}) diff --git a/spec/unit/search/search-by-number.spec.ts b/spec/unit/search/search-by-number.spec.ts deleted file mode 100644 index 21074f3..0000000 --- a/spec/unit/search/search-by-number.spec.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { Client } from "$lib/client" -import { Search, WhereField } from "$lib/search" - -import { A_NUMBER, ANOTHER_NUMBER } from '../../helpers/example-data' -import { simpleSchema } from "../helpers/test-entity-and-schema" - - -describe("Search", () => { - describe("#query", () => { - - let client: Client - let search: Search - let where: WhereField - - const AN_EQUAL_QUERY = `(@aNumber:[${A_NUMBER} ${A_NUMBER}])` - const A_NEGATED_EQUAL_QUERY = `(-@aNumber:[${A_NUMBER} ${A_NUMBER}])` - const A_GT_QUERY = `(@aNumber:[(${A_NUMBER} +inf])` - const A_NEGATED_GT_QUERY = `(-@aNumber:[(${A_NUMBER} +inf])` - const A_GTE_QUERY = `(@aNumber:[${A_NUMBER} +inf])` - const A_NEGATED_GTE_QUERY = `(-@aNumber:[${A_NUMBER} +inf])` - const AN_LT_QUERY = `(@aNumber:[-inf (${A_NUMBER}])` - const A_NEGATED_LT_QUERY = `(-@aNumber:[-inf (${A_NUMBER}])` - const AN_LTE_QUERY = `(@aNumber:[-inf ${A_NUMBER}])` - const A_NEGATED_LTE_QUERY = `(-@aNumber:[-inf ${A_NUMBER}])` - const A_BETWEEN_QUERY = `(@aNumber:[${ANOTHER_NUMBER} ${A_NUMBER}])` - const A_NEGATED_BETWEEN_QUERY = `(-@aNumber:[${ANOTHER_NUMBER} ${A_NUMBER}])` - - type RangeChecker = (search: Search) => void - const expectToBeEqualQuery: RangeChecker = search => expect(search.query).toBe(AN_EQUAL_QUERY) - const expectToBeNegatedEqualQuery: RangeChecker = search => expect(search.query).toBe(A_NEGATED_EQUAL_QUERY) - const expectToBeGTQuery: RangeChecker = search => expect(search.query).toBe(A_GT_QUERY) - const expectToBeNegatedGTQuery: RangeChecker = search => expect(search.query).toBe(A_NEGATED_GT_QUERY) - const expectToBeGTEQuery: RangeChecker = search => expect(search.query).toBe(A_GTE_QUERY) - const expectToBeNegatedGTEQuery: RangeChecker = search => expect(search.query).toBe(A_NEGATED_GTE_QUERY) - const expectToBeLTQuery: RangeChecker = search => expect(search.query).toBe(AN_LT_QUERY) - const expectToBeNegatedLTQuery: RangeChecker = search => expect(search.query).toBe(A_NEGATED_LT_QUERY) - const expectToBeLTEQuery: RangeChecker = search => expect(search.query).toBe(AN_LTE_QUERY) - const expectToBeNegatedLTEQuery: RangeChecker = search => expect(search.query).toBe(A_NEGATED_LTE_QUERY) - const expectToBeBetweenQuery: RangeChecker = search => expect(search.query).toBe(A_BETWEEN_QUERY) - const expectToBeNegatedBetweenQuery: RangeChecker = search => expect(search.query).toBe(A_NEGATED_BETWEEN_QUERY) - - beforeAll(() => { - client = new Client() - }) - - beforeEach(() => { - search = new Search(simpleSchema, client) - where = search.where('aNumber') - }) - - describe("when generating a query with a number", () => { - - it("generates a query with .eq", () => expectToBeEqualQuery(where.eq(A_NUMBER))) - it("generates a query with .not.eq", () => expectToBeNegatedEqualQuery(where.not.eq(A_NUMBER))) - it("generates a query with .equals", () => expectToBeEqualQuery(where.equals(A_NUMBER))) - it("generates a query with .does.equal", () => expectToBeEqualQuery(where.does.equal(A_NUMBER))) - it("generates a query with .does.not.equal", () => expectToBeNegatedEqualQuery(where.does.not.equal(A_NUMBER))) - it("generates a query with .is.equalTo", () => expectToBeEqualQuery(where.is.equalTo(A_NUMBER))) - it("generates a query with .is.not.equalTo", () => expectToBeNegatedEqualQuery(where.is.not.equalTo(A_NUMBER))) - - it("generates a query with .gt", () => expectToBeGTQuery(where.gt(A_NUMBER))) - it("generates a query with .not.gt", () => expectToBeNegatedGTQuery(where.not.gt(A_NUMBER))) - it("generates a query with .greaterThan", () => expectToBeGTQuery(where.greaterThan(A_NUMBER))) - it("generates a query with .is.greaterThan", () => expectToBeGTQuery(where.is.greaterThan(A_NUMBER))) - it("generates a query with .is.not.greaterThan", () => expectToBeNegatedGTQuery(where.is.not.greaterThan(A_NUMBER))) - - it("generates a query with .gte", () => expectToBeGTEQuery(where.gte(A_NUMBER))) - it("generates a query with .not.gte", () => expectToBeNegatedGTEQuery(where.not.gte(A_NUMBER))) - it("generates a query with .greaterThanOrEqualTo", () => expectToBeGTEQuery(where.greaterThanOrEqualTo(A_NUMBER))) - it("generates a query with .is.greaterThanOrEqualTo", () => expectToBeGTEQuery(where.is.greaterThanOrEqualTo(A_NUMBER))) - it("generates a query with .is.not.greaterThanOrEqualTo", () => expectToBeNegatedGTEQuery(where.is.not.greaterThanOrEqualTo(A_NUMBER))) - - it("generates a query with .lt", () => expectToBeLTQuery(where.lt(A_NUMBER))) - it("generates a query with .not.lt", () => expectToBeNegatedLTQuery(where.not.lt(A_NUMBER))) - it("generates a query with .lessThan", () => expectToBeLTQuery(where.lessThan(A_NUMBER))) - it("generates a query with .is.lessThan", () => expectToBeLTQuery(where.is.lessThan(A_NUMBER))) - it("generates a query with .is.not.lessThan", () => expectToBeNegatedLTQuery(where.is.not.lessThan(A_NUMBER))) - - it("generates a query with .lte", () => expectToBeLTEQuery(where.lte(A_NUMBER))) - it("generates a query with .not.lte", () => expectToBeNegatedLTEQuery(where.not.lte(A_NUMBER))) - it("generates a query with .lessThanOrEqualTo", () => expectToBeLTEQuery(where.lessThanOrEqualTo(A_NUMBER))) - it("generates a query with .is.lessThanOrEqualTo", () => expectToBeLTEQuery(where.is.lessThanOrEqualTo(A_NUMBER))) - it("generates a query with .is.not.lessThanOrEqualTo", () => expectToBeNegatedLTEQuery(where.is.not.lessThanOrEqualTo(A_NUMBER))) - - it("generates a query with .between", () => expectToBeBetweenQuery(where.between(ANOTHER_NUMBER, A_NUMBER))) - it("generates a query with .not.between", () => expectToBeNegatedBetweenQuery(where.not.between(ANOTHER_NUMBER, A_NUMBER))) - it("generates a query with .is.between", () => expectToBeBetweenQuery(where.is.between(ANOTHER_NUMBER, A_NUMBER))) - it("generates a query with .is.not.between", () => expectToBeNegatedBetweenQuery(where.is.not.between(ANOTHER_NUMBER, A_NUMBER))) - }) - }) -}) diff --git a/spec/unit/search/search-by-point.spec.ts b/spec/unit/search/search-by-point.spec.ts deleted file mode 100644 index 2b133db..0000000 --- a/spec/unit/search/search-by-point.spec.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { Point } from '../../../lib' - -import { Client } from "$lib/client" -import { Search, WhereField } from "$lib/search" - -import { A_POINT } from '../../helpers/example-data' -import { simpleSchema } from "../helpers/test-entity-and-schema" - - -describe("Search", () => { - describe("#query", () => { - - let client: Client - let search: Search - let where: WhereField - - const A_DEFAULT_QUERY = "(@aPoint:[0 0 1 m])" - const A_METERS_QUERY = "(@aPoint:[12.34 56.78 42 m])" - const A_NEGATED_METERS_QUERY = "(-@aPoint:[12.34 56.78 42 m])" - const A_KILOMETERS_QUERY = "(@aPoint:[12.34 56.78 42 km])" - const A_NEGATED_KILOMETERS_QUERY = "(-@aPoint:[12.34 56.78 42 km])" - const A_FEET_QUERY = "(@aPoint:[12.34 56.78 42 ft])" - const A_NEGATED_FEET_QUERY = "(-@aPoint:[12.34 56.78 42 ft])" - const A_MILES_QUERY = "(@aPoint:[12.34 56.78 42 mi])" - const A_NEGATED_MILES_QUERY = "(-@aPoint:[12.34 56.78 42 mi])" - - type GeoChecker = (search: Search) => void - const expectToBeDefaultQuery: GeoChecker = search => expect(search.query).toBe(A_DEFAULT_QUERY) - const expectToBeMetersQuery: GeoChecker = search => expect(search.query).toBe(A_METERS_QUERY) - const expectToBeNegatedMetersQuery: GeoChecker = search => expect(search.query).toBe(A_NEGATED_METERS_QUERY) - const expectToBeKilometersQuery: GeoChecker = search => expect(search.query).toBe(A_KILOMETERS_QUERY) - const expectToBeNegatedKilometersQuery: GeoChecker = search => expect(search.query).toBe(A_NEGATED_KILOMETERS_QUERY) - const expectToBeFeetQuery: GeoChecker = search => expect(search.query).toBe(A_FEET_QUERY) - const expectToBeNegatedFeetQuery: GeoChecker = search => expect(search.query).toBe(A_NEGATED_FEET_QUERY) - const expectToBeMilesQuery: GeoChecker = search => expect(search.query).toBe(A_MILES_QUERY) - const expectToBeNegatedMilesQuery: GeoChecker = search => expect(search.query).toBe(A_NEGATED_MILES_QUERY) - - const point: Point = A_POINT - const longitude = 12.34 - const latitude = 56.78 - - beforeAll(() => { - client = new Client() - }) - - beforeEach(() => { - search = new Search(simpleSchema, client) - where = search.where('aPoint') - }) - - describe("when generating a query with a point", () => { - - describe("and in a circle", () => { - - it("generates a query with default values using .inCircle", - () => expectToBeDefaultQuery(where.inCircle(circle => circle))) - it("generates a query with default values using .inRadius", - () => expectToBeDefaultQuery(where.inRadius(circle => circle))) - - it("generates a query with .point(point).radius.meters", - () => expectToBeMetersQuery(where.inCircle(circle => circle.origin(point).radius(42).meters))) - it("generates a query with .point(lng, lat).radius.meter", - () => expectToBeMetersQuery(where.is.inCircle(circle => circle.origin(longitude, latitude).radius(42).meter))) - it("generates a query with .longitude.latitude.radius.m", - () => expectToBeNegatedMetersQuery(where.is.not.inCircle(circle => circle.longitude(longitude).latitude(latitude).radius(42).m))) - - it("generates a query with .point(point).radius.kilometers", - () => expectToBeKilometersQuery(where.inCircle(circle => circle.origin(point).radius(42).kilometers))) - it("generates a query with .point(lng, lat).radius.kilometer", - () => expectToBeKilometersQuery(where.is.inCircle(circle => circle.origin(longitude, latitude).radius(42).kilometer))) - it("generates a query with .longitude.latitude.radius.km", - () => expectToBeNegatedKilometersQuery(where.is.not.inCircle(circle => circle.longitude(longitude).latitude(latitude).radius(42).km))) - - it("generates a query with .point(point).radius.feet", - () => expectToBeFeetQuery(where.inCircle(circle => circle.origin(point).radius(42).feet))) - it("generates a query with .point(lng, lat).radius.foot", - () => expectToBeFeetQuery(where.is.inCircle(circle => circle.origin(longitude, latitude).radius(42).foot))) - it("generates a query with .longitude.latitude.radius.ft", - () => expectToBeNegatedFeetQuery(where.is.not.inCircle(circle => circle.longitude(longitude).latitude(latitude).radius(42).ft))) - - it("generates a query with .point(point).radius.miles", - () => expectToBeMilesQuery(where.inCircle(circle => circle.origin(point).radius(42).miles))) - it("generates a query with .point(lng, lat).radius.mile", - () => expectToBeMilesQuery(where.is.inCircle(circle => circle.origin(longitude, latitude).radius(42).mile))) - it("generates a query with .longitude.latitude.radius.mi", - () => expectToBeNegatedMilesQuery(where.is.not.inCircle(circle => circle.longitude(longitude).latitude(latitude).radius(42).mi))) - }) - }) - }) -}) diff --git a/spec/unit/search/search-by-string-array.spec.ts b/spec/unit/search/search-by-string-array.spec.ts deleted file mode 100644 index 22fe6fe..0000000 --- a/spec/unit/search/search-by-string-array.spec.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { Client } from "$lib/client" -import { Search, WhereField } from "$lib/search" - -import { A_STRING, ANOTHER_STRING, A_THIRD_STRING } from '../../helpers/example-data' -import { simpleSchema } from "../helpers/test-entity-and-schema" - - -describe("Search", () => { - describe("#query", () => { - - let client: Client - let search: Search - let where: WhereField - - const A_CONTAINS_QUERY = `(@someStrings:{${A_STRING}})` - const A_NEGATED_CONTAINS_QUERY = `(-@someStrings:{${A_STRING}})` - const A_CONTAINS_ONE_QUERY = `(@someStrings:{${A_STRING}|${ANOTHER_STRING}|${A_THIRD_STRING}})` - const A_NEGATED_CONTAINS_ONE_QUERY = `(-@someStrings:{${A_STRING}|${ANOTHER_STRING}|${A_THIRD_STRING}})` - - type ArrayChecker = (search: Search) => void - const expectToBeContainsQuery: ArrayChecker = search => expect(search.query).toBe(A_CONTAINS_QUERY) - const expectToBeNegatedContainsQuery: ArrayChecker = search => expect(search.query).toBe(A_NEGATED_CONTAINS_QUERY) - const expectToBeContainsOneQuery: ArrayChecker = search => expect(search.query).toBe(A_CONTAINS_ONE_QUERY) - const expectToBeNegatedContainsOneQuery: ArrayChecker = search => expect(search.query).toBe(A_NEGATED_CONTAINS_ONE_QUERY) - - beforeAll(() => { - client = new Client() - }) - - beforeEach(() => { - search = new Search(simpleSchema, client) - where = search.where('someStrings') - }) - - describe("when generating for an array", () => { - - it("generates a query with .contains", () => expectToBeContainsQuery(where.contains(A_STRING))) - it("generates a query with .does.contain", () => expectToBeContainsQuery(where.does.contain(A_STRING))) - it("generates a query with .does.not.contain", () => expectToBeNegatedContainsQuery(where.does.not.contain(A_STRING))) - - it("generates a query with .containsOneOf", () => expectToBeContainsOneQuery(where.containsOneOf(A_STRING, ANOTHER_STRING, A_THIRD_STRING))) - it("generates a query with .does.containOneOf", () => expectToBeContainsOneQuery(where.does.containOneOf(A_STRING, ANOTHER_STRING, A_THIRD_STRING))) - it("generates a query with .does.not.containOneOf", () => expectToBeNegatedContainsOneQuery(where.does.not.containOneOf(A_STRING, ANOTHER_STRING, A_THIRD_STRING))) - - it("generates a query with .contains that escapes all punctuation", () => { - let query = where.contains(",.<>{}[]\"':;|!@#$%^&()-+=~ ").query - expect(query).toBe("(@someStrings:{\\,\\.\\<\\>\\{\\}\\[\\]\\\"\\'\\:\\;\\|\\!\\@\\#\\$\\%\\^\\&\\(\\)\\-\\+\\=\\~\\ })") - }) - - it("generates a query with .containsOneOf that escapes all punctuation", () => { - let query = where.containsOneOf(",.<>{}[]\"':;|", "!@#$%^&()-+=~ ").query - expect(query).toBe("(@someStrings:{\\,\\.\\<\\>\\{\\}\\[\\]\\\"\\'\\:\\;\\||\\!\\@\\#\\$\\%\\^\\&\\(\\)\\-\\+\\=\\~\\ })") - }) - - it("generates a query with .contains with a prefix matching wildcard", () => { - let query = where.contains("foo*").query - expect(query).toBe("(@someStrings:{foo*})") - }) - - it("generates a query with .containsOnOf with a prefix matching wildcard", () => { - let query = where.containsOneOf("foo*").query - expect(query).toBe("(@someStrings:{foo*})") - }) - }) - }) -}) diff --git a/spec/unit/search/search-by-string.spec.ts b/spec/unit/search/search-by-string.spec.ts deleted file mode 100644 index 796bfea..0000000 --- a/spec/unit/search/search-by-string.spec.ts +++ /dev/null @@ -1,104 +0,0 @@ -import '../../helpers/custom-matchers' - -import { Client } from "$lib/client" -import { SemanticSearchError } from "$lib/error" -import { Search, WhereField } from "$lib/search" - -import { A_STRING, A_NUMBER } from '../../helpers/example-data' -import { simpleSchema } from "../helpers/test-entity-and-schema" - - -describe("Search", () => { - describe("#query", () => { - - let client: Client - let search: Search - let where: WhereField - - const A_STRING_QUERY = `(@aString:{${A_STRING}})` - const A_NEGATED_STRING_QUERY = `(-@aString:{${A_STRING}})` - const A_NUMBER_STRING_QUERY = `(@aString:{${A_NUMBER}})` - const A_NEGATED_NUMBER_STRING_QUERY = `(-@aString:{${A_NUMBER}})` - const A_BOOLEAN_STRING_QUERY = `(@aString:{true})` - const A_NEGATED_BOOLEAN_STRING_QUERY = `(-@aString:{true})` - - type StringChecker = (search: Search) => void - const expectToBeStringQuery: StringChecker = search => expect(search.query).toBe(A_STRING_QUERY) - const expectToBeNegatedStringQuery: StringChecker = search => expect(search.query).toBe(A_NEGATED_STRING_QUERY) - const expectToBeNumberStringQuery: StringChecker = search => expect(search.query).toBe(A_NUMBER_STRING_QUERY) - const expectToBeNegatedNumberStringQuery: StringChecker = search => expect(search.query).toBe(A_NEGATED_NUMBER_STRING_QUERY) - const expectToBeBooleanStringQuery: StringChecker = search => expect(search.query).toBe(A_BOOLEAN_STRING_QUERY) - const expectToBeNegatedBooleanStringQuery: StringChecker = search => expect(search.query).toBe(A_NEGATED_BOOLEAN_STRING_QUERY) - - beforeAll(() => { - client = new Client() - }) - - beforeEach(() => { - search = new Search(simpleSchema, client) - where = search.where('aString') - }) - - describe("when generating a query with a string", () => { - it("generates a query with .eq", () => expectToBeStringQuery(where.equals(A_STRING))) - it("generates a query with .not.eq", () => expectToBeNegatedStringQuery(where.not.eq(A_STRING))) - it("generates a query with .equals", () => expectToBeStringQuery(where.equals(A_STRING))) - it("generates a query with .does.equal", () => expectToBeStringQuery(where.does.equal(A_STRING))) - it("generates a query with .does.not.equal", () => expectToBeNegatedStringQuery(where.does.not.equal(A_STRING))) - it("generates a query with .is.equalTo", () => expectToBeStringQuery(where.is.equalTo(A_STRING))) - it("generates a query with .is.not.equalTo", () => expectToBeNegatedStringQuery(where.is.not.equalTo(A_STRING))) - }) - - describe("when generating a query with a number as a string", () => { - it("generates a query with .eq", () => expectToBeNumberStringQuery(where.equals(A_NUMBER))) - it("generates a query with .not.eq", () => expectToBeNegatedNumberStringQuery(where.not.eq(A_NUMBER))) - it("generates a query with .equals", () => expectToBeNumberStringQuery(where.equals(A_NUMBER))) - it("generates a query with .does.equal", () => expectToBeNumberStringQuery(where.does.equal(A_NUMBER))) - it("generates a query with .does.not.equal", () => expectToBeNegatedNumberStringQuery(where.does.not.equal(A_NUMBER))) - it("generates a query with .is.equalTo", () => expectToBeNumberStringQuery(where.is.equalTo(A_NUMBER))) - it("generates a query with .is.not.equalTo", () => expectToBeNegatedNumberStringQuery(where.is.not.equalTo(A_NUMBER))) - }) - - describe("when generating a query with a boolean as a string", () => { - it("generates a query with .eq", () => expectToBeBooleanStringQuery(where.equals(true))) - it("generates a query with .not.eq", () => expectToBeNegatedBooleanStringQuery(where.not.eq(true))) - it("generates a query with .equals", () => expectToBeBooleanStringQuery(where.equals(true))) - it("generates a query with .does.equal", () => expectToBeBooleanStringQuery(where.does.equal(true))) - it("generates a query with .does.not.equal", () => expectToBeNegatedBooleanStringQuery(where.does.not.equal(true))) - it("generates a query with .is.equalTo", () => expectToBeBooleanStringQuery(where.is.equalTo(true))) - it("generates a query with .is.not.equalTo", () => expectToBeNegatedBooleanStringQuery(where.is.not.equalTo(true))) - }) - - describe("when generating a query with special characters in the string", () => { - it("generates a query that escapes punctuation between text", () => { - let query = where.eq('foo,bar baz').query - expect(query).toBe("(@aString:{foo\\,bar\\ baz})") - }) - - it("generates a query that escapes all punctuation", () => { - let query = where.eq(",.<>{}[]\"':;!@#$%^&()-+=~|/\\ ").query - expect(query).toBe("(@aString:{\\,\\.\\<\\>\\{\\}\\[\\]\\\"\\'\\:\\;\\!\\@\\#\\$\\%\\^\\&\\(\\)\\-\\+\\=\\~\\|\\/\\\\\\ })") - }) - - it("generates a query with a prefix matching wildcard", () => { - let query = where.eq("foo*").query - expect(query).toBe("(@aString:{foo*})") - }) - }) - - describe("when trying to perform full-text search on a string", () => { - const EXPECTED_EXCEPTION = "Cannot perform full-text search operations like .match on field of type 'string'. If full-text search is needed on this field, change the type to 'text' in the Schema." - it("throws an exception telling you what to do", () => { - expect(() => where.match(A_STRING)).toThrowErrorOfType(SemanticSearchError, EXPECTED_EXCEPTION) - expect(() => where.matchExact(A_STRING)).toThrowErrorOfType(SemanticSearchError, EXPECTED_EXCEPTION) - expect(() => where.exact.match(A_STRING)).toThrowErrorOfType(SemanticSearchError, EXPECTED_EXCEPTION) - expect(() => where.exact.matches(A_STRING)).toThrowErrorOfType(SemanticSearchError, EXPECTED_EXCEPTION) - expect(() => where.matchExactly(A_STRING)).toThrowErrorOfType(SemanticSearchError, EXPECTED_EXCEPTION) - expect(() => where.matches(A_STRING)).toThrowErrorOfType(SemanticSearchError, EXPECTED_EXCEPTION) - expect(() => where.matchesExactly(A_STRING)).toThrowErrorOfType(SemanticSearchError, EXPECTED_EXCEPTION) - expect(() => where.exactly.match(A_STRING)).toThrowErrorOfType(SemanticSearchError, EXPECTED_EXCEPTION) - expect(() => where.exactly.matches(A_STRING)).toThrowErrorOfType(SemanticSearchError, EXPECTED_EXCEPTION) - }) - }) - }) -}) diff --git a/spec/unit/search/search-by-text.spec.ts b/spec/unit/search/search-by-text.spec.ts deleted file mode 100644 index c168fe8..0000000 --- a/spec/unit/search/search-by-text.spec.ts +++ /dev/null @@ -1,140 +0,0 @@ -import '../../helpers/custom-matchers' - -import { Client } from "$lib/client" -import { SemanticSearchError } from "$lib/error" -import { Search, WhereField } from "$lib/search" - -import { A_STRING , A_NUMBER } from '../../helpers/example-data' -import { simpleSchema } from "../helpers/test-entity-and-schema" - - -describe("Search", () => { - describe("#query", () => { - - let client: Client - let search: Search - let where: WhereField - - const A_TEXT_QUERY = `(@someText:'${A_STRING}')` - const A_NEGATED_TEXT_QUERY = `(-@someText:'${A_STRING}')` - const AN_EXACT_TEXT_QUERY = `(@someText:"${A_STRING}")` - const A_NEGATED_EXACT_TEXT_QUERY = `(-@someText:"${A_STRING}")` - - const A_NUMBER_TEXT_QUERY = `(@someText:'${A_NUMBER}')` - const A_NEGATED_NUMBER_TEXT_QUERY = `(-@someText:'${A_NUMBER}')` - const A_NUMBER_EXACT_TEXT_QUERY = `(@someText:"${A_NUMBER}")` - const A_NEGATED_NUMBER_EXACT_TEXT_QUERY = `(-@someText:"${A_NUMBER}")` - - const A_BOOLEAN_TEXT_QUERY = `(@someText:'true')` - const A_NEGATED_BOOLEAN_TEXT_QUERY = `(-@someText:'true')` - const A_BOOLEAN_EXACT_TEXT_QUERY = `(@someText:"true")` - const A_NEGATED_BOOLEAN_EXACT_TEXT_QUERY = `(-@someText:"true")` - - type StringChecker = (search: Search) => void - const expectToBeTextQuery: StringChecker = search => expect(search.query).toBe(A_TEXT_QUERY) - const expectToBeNegatedTextQuery: StringChecker = search => expect(search.query).toBe(A_NEGATED_TEXT_QUERY) - const expectToBeExactTextQuery: StringChecker = search => expect(search.query).toBe(AN_EXACT_TEXT_QUERY) - const expectToBeNegatedExactTextQuery: StringChecker = search => expect(search.query).toBe(A_NEGATED_EXACT_TEXT_QUERY) - - const expectToBeNumberTextQuery: StringChecker = search => expect(search.query).toBe(A_NUMBER_TEXT_QUERY) - const expectToBeNegatedNumberTextQuery: StringChecker = search => expect(search.query).toBe(A_NEGATED_NUMBER_TEXT_QUERY) - const expectToBeNumberExactTextQuery: StringChecker = search => expect(search.query).toBe(A_NUMBER_EXACT_TEXT_QUERY) - const expectToBeNegatedNumberExactTextQuery: StringChecker = search => expect(search.query).toBe(A_NEGATED_NUMBER_EXACT_TEXT_QUERY) - - const expectToBeBooleanTextQuery: StringChecker = search => expect(search.query).toBe(A_BOOLEAN_TEXT_QUERY) - const expectToBeNegatedBooleanTextQuery: StringChecker = search => expect(search.query).toBe(A_NEGATED_BOOLEAN_TEXT_QUERY) - const expectToBeBooleanExactTextQuery: StringChecker = search => expect(search.query).toBe(A_BOOLEAN_EXACT_TEXT_QUERY) - const expectToBeNegatedBooleanExactTextQuery: StringChecker = search => expect(search.query).toBe(A_NEGATED_BOOLEAN_EXACT_TEXT_QUERY) - - beforeAll(() => { - client = new Client() - }) - - beforeEach(() => { - search = new Search(simpleSchema, client) - where = search.where('someText') - }) - - describe("when generating for a query with a string", () => { - it("generates a query with .match", () => expectToBeTextQuery(where.match(A_STRING))) - it("generates a query with .not.match", () => expectToBeNegatedTextQuery(where.not.match(A_STRING))) - it("generates a query with .matches", () => expectToBeTextQuery(where.matches(A_STRING))) - it("generates a query with .does.match", () => expectToBeTextQuery(where.does.match(A_STRING))) - it("generates a query with .does.not.match", () => expectToBeNegatedTextQuery(where.does.not.match(A_STRING))) - - it("generates a query with .exact.match", () => expectToBeExactTextQuery(where.exact.match(A_STRING))) - it("generates a query with .not.exact.match", () => expectToBeNegatedExactTextQuery(where.not.exact.match(A_STRING))) - it("generates a query with .exactly.matches", () => expectToBeExactTextQuery(where.exactly.matches(A_STRING))) - it("generates a query with .does.exactly.match", () => expectToBeExactTextQuery(where.does.exactly.match(A_STRING))) - it("generates a query with .does.not.exactly.match", () => expectToBeNegatedExactTextQuery(where.does.not.exactly.match(A_STRING))) - - it("generates a query with .matchExact", () => expectToBeExactTextQuery(where.matchExact(A_STRING))) - it("generates a query with .not.matchExact", () => expectToBeNegatedExactTextQuery(where.not.matchExact(A_STRING))) - it("generates a query with .matchesExactly", () => expectToBeExactTextQuery(where.matchesExactly(A_STRING))) - it("generates a query with .does.matchExactly", () => expectToBeExactTextQuery(where.does.matchExactly(A_STRING))) - it("generates a query with .does.not.matchExactly", () => expectToBeNegatedExactTextQuery(where.does.not.matchExactly(A_STRING))) - }) - - describe("when generating a query with a number as a string", () => { - it("generates a query with .match", () => expectToBeNumberTextQuery(where.match(A_NUMBER))) - it("generates a query with .not.match", () => expectToBeNegatedNumberTextQuery(where.not.match(A_NUMBER))) - it("generates a query with .matches", () => expectToBeNumberTextQuery(where.matches(A_NUMBER))) - it("generates a query with .does.match", () => expectToBeNumberTextQuery(where.does.match(A_NUMBER))) - it("generates a query with .does.not.match", () => expectToBeNegatedNumberTextQuery(where.does.not.match(A_NUMBER))) - - it("generates a query with .exact.match", () => expectToBeNumberExactTextQuery(where.exact.match(A_NUMBER))) - it("generates a query with .not.exact.match", () => expectToBeNegatedNumberExactTextQuery(where.not.exact.match(A_NUMBER))) - it("generates a query with .exactly.matches", () => expectToBeNumberExactTextQuery(where.exactly.matches(A_NUMBER))) - it("generates a query with .does.exactly.match", () => expectToBeNumberExactTextQuery(where.does.exactly.match(A_NUMBER))) - it("generates a query with .does.not.exactly.match", () => expectToBeNegatedNumberExactTextQuery(where.does.not.exactly.match(A_NUMBER))) - - it("generates a query with .matchExact", () => expectToBeNumberExactTextQuery(where.matchExact(A_NUMBER))) - it("generates a query with .not.matchExact", () => expectToBeNegatedNumberExactTextQuery(where.not.matchExact(A_NUMBER))) - it("generates a query with .matchesExactly", () => expectToBeNumberExactTextQuery(where.matchesExactly(A_NUMBER))) - it("generates a query with .does.matchExactly", () => expectToBeNumberExactTextQuery(where.does.matchExactly(A_NUMBER))) - it("generates a query with .does.not.matchExactly", () => expectToBeNegatedNumberExactTextQuery(where.does.not.matchExactly(A_NUMBER))) - }) - - describe("when generating a query with a boolean as a string", () => { - it("generates a query with .match", () => expectToBeBooleanTextQuery(where.match(true))) - it("generates a query with .not.match", () => expectToBeNegatedBooleanTextQuery(where.not.match(true))) - it("generates a query with .matches", () => expectToBeBooleanTextQuery(where.matches(true))) - it("generates a query with .does.match", () => expectToBeBooleanTextQuery(where.does.match(true))) - it("generates a query with .does.not.match", () => expectToBeNegatedBooleanTextQuery(where.does.not.match(true))) - - it("generates a query with .exact.match", () => expectToBeBooleanExactTextQuery(where.exact.match(true))) - it("generates a query with .not.exact.match", () => expectToBeNegatedBooleanExactTextQuery(where.not.exact.match(true))) - it("generates a query with .exactly.matches", () => expectToBeBooleanExactTextQuery(where.exactly.matches(true))) - it("generates a query with .does.exactly.match", () => expectToBeBooleanExactTextQuery(where.does.exactly.match(true))) - it("generates a query with .does.not.exactly.match", () => expectToBeNegatedBooleanExactTextQuery(where.does.not.exactly.match(true))) - - it("generates a query with .matchExact", () => expectToBeBooleanExactTextQuery(where.matchExact(true))) - it("generates a query with .not.matchExact", () => expectToBeNegatedBooleanExactTextQuery(where.not.matchExact(true))) - it("generates a query with .matchesExactly", () => expectToBeBooleanExactTextQuery(where.matchesExactly(true))) - it("generates a query with .does.matchExactly", () => expectToBeBooleanExactTextQuery(where.does.matchExactly(true))) - it("generates a query with .does.not.matchExactly", () => expectToBeNegatedBooleanExactTextQuery(where.does.not.matchExactly(true))) - }) - - describe("when generating a query with special characters in the string", () => { - it("generates a query that escapes all punctuation for a match", () => { - let query = where.match(",.<>{}[]\"':;!@#$%^&()-+=~|").query - expect(query).toBe("(@someText:'\\,\\.\\<\\>\\{\\}\\[\\]\\\"\\'\\:\\;\\!\\@\\#\\$\\%\\^\\&\\(\\)\\-\\+\\=\\~\\|')") - }) - - it("generates a query that escapes all punctuation for an exact match", () => { - let query = where.exact.match(",.<>{}[]\"':;!@#$%^&()-+=~|").query - expect(query).toBe('(@someText:"\\,\\.\\<\\>\\{\\}\\[\\]\\"\\\'\\:\\;\\!\\@\\#\\$\\%\\^\\&\\(\\)\\-\\+\\=\\~\\|")') - }) - }) - - describe("when trying to perform string equality full-text", () => { - const EXPECTED_EXCEPTION = "Cannot call .equals on a field of type 'text', either use .match to perform full-text search or change the type to 'string' in the Schema." - it("throws an exception telling you what to do", () => { - expect(() => where.eq(A_STRING)).toThrowErrorOfType(SemanticSearchError, EXPECTED_EXCEPTION) - expect(() => where.equal(A_STRING)).toThrowErrorOfType(SemanticSearchError, EXPECTED_EXCEPTION) - expect(() => where.equals(A_STRING)).toThrowErrorOfType(SemanticSearchError, EXPECTED_EXCEPTION) - expect(() => where.equalTo(A_STRING)).toThrowErrorOfType(SemanticSearchError, EXPECTED_EXCEPTION) - }) - }) - }) -}) diff --git a/spec/unit/search/search-query.spec.ts b/spec/unit/search/search-query.spec.ts deleted file mode 100644 index 003de4e..0000000 --- a/spec/unit/search/search-query.spec.ts +++ /dev/null @@ -1,281 +0,0 @@ -import '../../helpers/custom-matchers' - -import { Client } from "$lib/client" -import { Search } from "$lib/search" - -import { simpleHashSchema, simpleJsonSchema } from "../helpers/test-entity-and-schema" -import { FieldNotInSchema } from "$lib/error" - -import { - A_STRING, ANOTHER_STRING, A_THIRD_STRING, - A_NUMBER, ANOTHER_NUMBER, A_THIRD_NUMBER, - A_DATE, A_DATE_EPOCH, A_POINT } from '../../helpers/example-data' - - -const POINT_LONGITUDE = A_POINT.longitude -const POINT_LATITUDE = A_POINT.latitude -const POINT_RADIUS = ANOTHER_NUMBER -const POINT_UNITS = 'mi' - -const EXPECTED_STRING_QUERY_1 = `@aString:{${A_STRING}}` -const EXPECTED_STRING_QUERY_2 = `@aString:{${ANOTHER_STRING}}` -const EXPECTED_STRING_QUERY_3 = `@aString:{${A_THIRD_STRING}}` - -const EXPECTED_NUMBER_QUERY_1 = `@aNumber:[${A_NUMBER} ${A_NUMBER}]` -const EXPECTED_NUMBER_QUERY_2 = `@aNumber:[${ANOTHER_NUMBER} ${ANOTHER_NUMBER}]` -const EXPECTED_NUMBER_QUERY_3 = `@aNumber:[${A_THIRD_NUMBER} ${A_THIRD_NUMBER}]` - -const EXPECTED_FALSE_HASH_QUERY = `@aBoolean:{0}` -const EXPECTED_FALSE_JSON_QUERY = `@aBoolean:{false}` -const EXPECTED_TRUE_HASH_QUERY = `@aBoolean:{1}` -const EXPECTED_TRUE_JSON_QUERY = `@aBoolean:{true}` - -const EXPECTED_TEXT_QUERY = `@someText:'${A_STRING}'` -const EXPECTED_POINT_QUERY = `@aPoint:[${POINT_LONGITUDE} ${POINT_LATITUDE} ${POINT_RADIUS} ${POINT_UNITS}]` -const EXPECTED_DATE_QUERY = `@aDate:[${A_DATE_EPOCH} +inf]` -const EXPECTED_ARRAY_QUERY = `@someStrings:{${A_STRING}|${ANOTHER_STRING}}` - -describe("Search", () => { - describe("#query", () => { - - let client: Client - - beforeAll(() => { - client = new Client() - }) - - describe("when querying against hashes", () => { - - let search: Search - - beforeEach(() => { - search = new Search(simpleHashSchema, client) - }) - - it("generates a query matching all items", () => { - expect(search.query).toBe("*") - }) - - it("throws an exception when invoked with a missing field", () => { - expect(() => search.where('missingString')) - .toThrowErrorOfType(FieldNotInSchema, "The field 'missingString' is not part of the schema and thus cannot be used to search.") - }) - - it("generates a query using .where", () => { - let query = search - .where('aString').eq(A_STRING) - .where('aNumber').eq(A_NUMBER) - .where('aBoolean').true() - .where('someText').matches(A_STRING) - .where('aPoint').inCircle(circle => circle.origin(A_POINT).radius(POINT_RADIUS).miles) - .where('aDate').onOrAfter(A_DATE) - .where('someStrings').containsOneOf(A_STRING, ANOTHER_STRING).query - expect(query).toBe(`( ( ( ( ( ( (${EXPECTED_STRING_QUERY_1}) (${EXPECTED_NUMBER_QUERY_1}) ) (${EXPECTED_TRUE_HASH_QUERY}) ) (${EXPECTED_TEXT_QUERY}) ) (${EXPECTED_POINT_QUERY}) ) (${EXPECTED_DATE_QUERY}) ) (${EXPECTED_ARRAY_QUERY}) )`) - }) - - it("generates a query using .and", () => { - let query = search - .where('aString').eq(A_STRING) - .and('aNumber').eq(A_NUMBER) - .and('aBoolean').true() - .and('someText').matches(A_STRING) - .and('aPoint').inCircle(circle => circle.origin(A_POINT).radius(POINT_RADIUS).miles) - .and('aDate').onOrAfter(A_DATE) - .and('someStrings').containsOneOf(A_STRING, ANOTHER_STRING).query - expect(query).toBe(`( ( ( ( ( ( (${EXPECTED_STRING_QUERY_1}) (${EXPECTED_NUMBER_QUERY_1}) ) (${EXPECTED_TRUE_HASH_QUERY}) ) (${EXPECTED_TEXT_QUERY}) ) (${EXPECTED_POINT_QUERY}) ) (${EXPECTED_DATE_QUERY}) ) (${EXPECTED_ARRAY_QUERY}) )`) - }) - - it("generates a query using .or", () => { - let query = search - .where('aString').eq(A_STRING) - .or('aNumber').equals(A_NUMBER) - .or('aBoolean').true() - .or('someText').matches(A_STRING) - .or('aPoint').inCircle(circle => circle.origin(A_POINT).radius(POINT_RADIUS).miles) - .or('aDate').onOrAfter(A_DATE) - .or('someStrings').containsOneOf(A_STRING, ANOTHER_STRING).query - expect(query).toBe(`( ( ( ( ( ( (${EXPECTED_STRING_QUERY_1}) | (${EXPECTED_NUMBER_QUERY_1}) ) | (${EXPECTED_TRUE_HASH_QUERY}) ) | (${EXPECTED_TEXT_QUERY}) ) | (${EXPECTED_POINT_QUERY}) ) | (${EXPECTED_DATE_QUERY}) ) | (${EXPECTED_ARRAY_QUERY}) )`) - }) - - it("generates a query using .and and .or", () => { - let query = search - .where('aString').eq(A_STRING) - .and('aNumber').equals(A_NUMBER) - .or('aBoolean').true().query - expect(query).toBe(`( ( (${EXPECTED_STRING_QUERY_1}) (${EXPECTED_NUMBER_QUERY_1}) ) | (${EXPECTED_TRUE_HASH_QUERY}) )`) - }) - - it("generates a query using .and with a function", () => { - let query = search - .where('aString').eq(A_STRING) - .and(search => search - .where('aString').eq(ANOTHER_STRING) - .and('aNumber').equals(A_NUMBER) - .or('aBoolean').true()).query - expect(query).toBe(`( (${EXPECTED_STRING_QUERY_1}) ( ( (${EXPECTED_STRING_QUERY_2}) (${EXPECTED_NUMBER_QUERY_1}) ) | (${EXPECTED_TRUE_HASH_QUERY}) ) )`) - }) - - it("generates a query using .or with a function", () => { - let query = search - .where('aString').eq(A_STRING) - .or(search => search - .where('aString').eq(ANOTHER_STRING) - .and('aNumber').equals(A_NUMBER) - .or('aBoolean').true()).query - expect(query).toBe(`( (${EXPECTED_STRING_QUERY_1}) | ( ( (${EXPECTED_STRING_QUERY_2}) (${EXPECTED_NUMBER_QUERY_1}) ) | (${EXPECTED_TRUE_HASH_QUERY}) ) )`) - }) - - it("generates a query using .where with a function", () => { - let query = search - .where(search => search - .where('aString').eq(A_STRING) - .and('aNumber').equals(A_NUMBER) - .or('aBoolean').true()).query - expect(query).toBe(`( ( (${EXPECTED_STRING_QUERY_1}) (${EXPECTED_NUMBER_QUERY_1}) ) | (${EXPECTED_TRUE_HASH_QUERY}) )`) - }) - - it("generates a complex query using all the things", () => { - let query = search - .where('aString').eq(A_STRING) - .and('aNumber').equals(A_NUMBER) - .or('aBoolean').true() - .and('someText').matches(A_STRING) - .and('aPoint').inCircle(circle => circle.origin(A_POINT).radius(POINT_RADIUS).miles) - .or('aDate').onOrAfter(A_DATE) - .and(search => search - .where('aString').eq(ANOTHER_STRING) - .and('aNumber').equals(ANOTHER_NUMBER) - .and('aBoolean').false() - ) - .or(search => search - .where('aString').eq(A_THIRD_STRING) - .or('aNumber').equals(A_THIRD_NUMBER) - .or('aBoolean').true() - .and(search => search - .where('aString').eq(A_STRING) - .and('aNumber').equals(A_NUMBER) - ) - ).query - - expect(query).toBe(`( ( ( ( ( ( ( (${EXPECTED_STRING_QUERY_1}) (${EXPECTED_NUMBER_QUERY_1}) ) | (${EXPECTED_TRUE_HASH_QUERY}) ) (${EXPECTED_TEXT_QUERY}) ) (${EXPECTED_POINT_QUERY}) ) | (${EXPECTED_DATE_QUERY}) ) ( ( (${EXPECTED_STRING_QUERY_2}) (${EXPECTED_NUMBER_QUERY_2}) ) (${EXPECTED_FALSE_HASH_QUERY}) ) ) | ( ( ( (${EXPECTED_STRING_QUERY_3}) | (${EXPECTED_NUMBER_QUERY_3}) ) | (${EXPECTED_TRUE_HASH_QUERY}) ) ( (${EXPECTED_STRING_QUERY_1}) (${EXPECTED_NUMBER_QUERY_1}) ) ) )`) - }) - }) - - describe("when querying against JSON objects", () => { - - let search: Search - - beforeEach(() => { - search = new Search(simpleJsonSchema, client) - }) - - it("generates a query matching all items", () => { - expect(search.query).toBe("*") - }) - - it("throws an exception when invoked with a missing field", () => { - expect(() => search.where('missingString')) - .toThrowErrorOfType(FieldNotInSchema, "The field 'missingString' is not part of the schema and thus cannot be used to search.") - }) - - it("generates a query using .where", () => { - let query = search - .where('aString').eq(A_STRING) - .where('aNumber').eq(A_NUMBER) - .where('aBoolean').true() - .where('someText').matches(A_STRING) - .where('aPoint').inCircle(circle => circle.origin(A_POINT).radius(POINT_RADIUS).miles) - .where('aDate').onOrAfter(A_DATE) - .where('someStrings').containsOneOf(A_STRING, ANOTHER_STRING).query - expect(query).toBe(`( ( ( ( ( ( (${EXPECTED_STRING_QUERY_1}) (${EXPECTED_NUMBER_QUERY_1}) ) (${EXPECTED_TRUE_JSON_QUERY}) ) (${EXPECTED_TEXT_QUERY}) ) (${EXPECTED_POINT_QUERY}) ) (${EXPECTED_DATE_QUERY}) ) (${EXPECTED_ARRAY_QUERY}) )`) - }) - - it("generates a query using .and", () => { - let query = search - .where('aString').eq(A_STRING) - .and('aNumber').eq(A_NUMBER) - .and('aBoolean').true() - .and('someText').matches(A_STRING) - .and('aPoint').inCircle(circle => circle.origin(A_POINT).radius(POINT_RADIUS).miles) - .and('aDate').onOrAfter(A_DATE) - .and('someStrings').containsOneOf(A_STRING, ANOTHER_STRING).query - expect(query).toBe(`( ( ( ( ( ( (${EXPECTED_STRING_QUERY_1}) (${EXPECTED_NUMBER_QUERY_1}) ) (${EXPECTED_TRUE_JSON_QUERY}) ) (${EXPECTED_TEXT_QUERY}) ) (${EXPECTED_POINT_QUERY}) ) (${EXPECTED_DATE_QUERY}) ) (${EXPECTED_ARRAY_QUERY}) )`) - }) - - it("generates a query using .or", () => { - let query = search - .where('aString').eq(A_STRING) - .or('aNumber').equals(A_NUMBER) - .or('aBoolean').true() - .or('someText').matches(A_STRING) - .or('aPoint').inCircle(circle => circle.origin(A_POINT).radius(POINT_RADIUS).miles) - .or('aDate').onOrAfter(A_DATE) - .or('someStrings').containsOneOf(A_STRING, ANOTHER_STRING).query - expect(query).toBe(`( ( ( ( ( ( (${EXPECTED_STRING_QUERY_1}) | (${EXPECTED_NUMBER_QUERY_1}) ) | (${EXPECTED_TRUE_JSON_QUERY}) ) | (${EXPECTED_TEXT_QUERY}) ) | (${EXPECTED_POINT_QUERY}) ) | (${EXPECTED_DATE_QUERY}) ) | (${EXPECTED_ARRAY_QUERY}) )`) - }) - - it("generates a query using .and and .or", () => { - let query = search - .where('aString').eq(A_STRING) - .and('aNumber').equals(A_NUMBER) - .or('aBoolean').true().query - expect(query).toBe(`( ( (${EXPECTED_STRING_QUERY_1}) (${EXPECTED_NUMBER_QUERY_1}) ) | (${EXPECTED_TRUE_JSON_QUERY}) )`) - }) - - it("generates a query using .and with a function", () => { - let query = search - .where('aString').eq(A_STRING) - .and(search => search - .where('aString').eq(ANOTHER_STRING) - .and('aNumber').equals(A_NUMBER) - .or('aBoolean').true()).query - expect(query).toBe(`( (${EXPECTED_STRING_QUERY_1}) ( ( (${EXPECTED_STRING_QUERY_2}) (${EXPECTED_NUMBER_QUERY_1}) ) | (${EXPECTED_TRUE_JSON_QUERY}) ) )`) - }) - - it("generates a query using .or with a function", () => { - let query = search - .where('aString').eq(A_STRING) - .or(search => search - .where('aString').eq(ANOTHER_STRING) - .and('aNumber').equals(A_NUMBER) - .or('aBoolean').true()).query - expect(query).toBe(`( (${EXPECTED_STRING_QUERY_1}) | ( ( (${EXPECTED_STRING_QUERY_2}) (${EXPECTED_NUMBER_QUERY_1}) ) | (${EXPECTED_TRUE_JSON_QUERY}) ) )`) - }) - - it("generates a query using .where with a function", () => { - let query = search - .where(search => search - .where('aString').eq(A_STRING) - .and('aNumber').equals(A_NUMBER) - .or('aBoolean').true()).query - expect(query).toBe(`( ( (${EXPECTED_STRING_QUERY_1}) (${EXPECTED_NUMBER_QUERY_1}) ) | (${EXPECTED_TRUE_JSON_QUERY}) )`) - }) - - it("generates a complex query using all the things", () => { - let query = search - .where('aString').eq(A_STRING) - .and('aNumber').equals(A_NUMBER) - .or('aBoolean').true() - .and('someText').matches(A_STRING) - .and('aPoint').inCircle(circle => circle.origin(A_POINT).radius(POINT_RADIUS).miles) - .or('aDate').onOrAfter(A_DATE) - .and(search => search - .where('aString').eq(ANOTHER_STRING) - .and('aNumber').equals(ANOTHER_NUMBER) - .and('aBoolean').false() - ) - .or(search => search - .where('aString').eq(A_THIRD_STRING) - .or('aNumber').equals(A_THIRD_NUMBER) - .or('aBoolean').true() - .and(search => search - .where('aString').eq(A_STRING) - .and('aNumber').equals(A_NUMBER) - ) - ).query - - expect(query).toBe(`( ( ( ( ( ( ( (${EXPECTED_STRING_QUERY_1}) (${EXPECTED_NUMBER_QUERY_1}) ) | (${EXPECTED_TRUE_JSON_QUERY}) ) (${EXPECTED_TEXT_QUERY}) ) (${EXPECTED_POINT_QUERY}) ) | (${EXPECTED_DATE_QUERY}) ) ( ( (${EXPECTED_STRING_QUERY_2}) (${EXPECTED_NUMBER_QUERY_2}) ) (${EXPECTED_FALSE_JSON_QUERY}) ) ) | ( ( ( (${EXPECTED_STRING_QUERY_3}) | (${EXPECTED_NUMBER_QUERY_3}) ) | (${EXPECTED_TRUE_JSON_QUERY}) ) ( (${EXPECTED_STRING_QUERY_1}) (${EXPECTED_NUMBER_QUERY_1}) ) ) )`) - }) - }) - }) -}) diff --git a/spec/unit/search/search-return-all-ids.spec.ts b/spec/unit/search/search-return-all-ids.spec.ts deleted file mode 100644 index 90e0023..0000000 --- a/spec/unit/search/search-return-all-ids.spec.ts +++ /dev/null @@ -1,117 +0,0 @@ -import { client } from '../helpers/mock-client' -import { Client } from "$lib/client" -import { EntityId } from '$lib/entity' -import { Search, RawSearch } from "$lib/search" - -import { simpleHashSchema } from "../helpers/test-entity-and-schema" -import { mockClientSearchToReturnNothing, mockClientSearchToReturnSingleKey, mockClientSearchToReturnMultipleKeys, - mockClientSearchToReturnPaginatedKeys, - SIMPLE_ENTITY_1, SIMPLE_ENTITY_2, SIMPLE_ENTITY_3, SIMPLE_ENTITY_4, SIMPLE_ENTITY_5 } from '../helpers/search-helpers' - - -type HashSearch = Search | RawSearch - -describe("Search", () => { - - describe.each([ - [ "FluentSearch", - new Search(simpleHashSchema, new Client()) ], - [ "RawSearch", - new RawSearch(simpleHashSchema, new Client()) ] - ])("%s", (_, search: HashSearch) => { - - describe("#returnAllIds", () => { - let ids: string[] - let indexName = 'SimpleHashEntity:index', query = '*' - - describe("when querying no results", () => { - beforeEach(async () => { - mockClientSearchToReturnNothing() - ids = await search.return.allIds() - }) - - it("askes the client for a single page of results", () => { - expect(client.search).toHaveBeenCalledTimes(1) - expect(client.search).toHaveBeenCalledWith(indexName, query, { - LIMIT: { from: 0, size: 10 }, - RETURN: [] - }) - }) - - it("returns no results", () => expect(ids).toHaveLength(0)) - }) - - describe("when querying a single result", () => { - beforeEach(async () => { - mockClientSearchToReturnSingleKey() - ids = await search.return.allIds() - }) - - it("askes the client for a a single page of results", () => { - expect(client.search).toHaveBeenCalledTimes(1) - expect(client.search).toHaveBeenCalledWith(indexName, query, { - LIMIT: { from: 0, size: 10 }, - RETURN: [] - }) - }) - - it("returns the expected single result", () => { - expect(ids).toHaveLength(1) - expect(ids).toEqual(expect.arrayContaining([ - SIMPLE_ENTITY_1[EntityId] - ])) - }) - }) - - describe("when querying multiple results", () => { - beforeEach(async () => { - mockClientSearchToReturnMultipleKeys() - ids = await search.return.allIds() - }) - - it("askes the client for a single page of results", () => { - expect(client.search).toHaveBeenCalledTimes(1) - expect(client.search).toHaveBeenCalledWith(indexName, query, { - LIMIT: { from: 0, size: 10 }, - RETURN: [] - }) - }) - - it("returns all the results", async () => { - expect(ids).toHaveLength(3) - expect(ids).toEqual(expect.arrayContaining([ - SIMPLE_ENTITY_1[EntityId], - SIMPLE_ENTITY_2[EntityId], - SIMPLE_ENTITY_3[EntityId] - ])) - }) - }) - - describe("when querying multiple results that cross the page boundry", () => { - beforeEach(async () => { - mockClientSearchToReturnPaginatedKeys() - ids = await search.return.allIds({ pageSize: 2 }) - }) - - it("askes the client for multiple pages of results", () => { - expect(client.search).toHaveBeenCalledTimes(3) - expect(client.search).toHaveBeenCalledWith(indexName, query, { - LIMIT: { from: 0, size: 2 }, - RETURN: [] - }) - }) - - it("returns all the results", async () => { - expect(ids).toHaveLength(5) - expect(ids).toEqual(expect.arrayContaining([ - SIMPLE_ENTITY_1[EntityId], - SIMPLE_ENTITY_2[EntityId], - SIMPLE_ENTITY_3[EntityId], - SIMPLE_ENTITY_4[EntityId], - SIMPLE_ENTITY_5[EntityId] - ])) - }) - }) - }) - }) -}) diff --git a/spec/unit/search/search-return-all-keys.spec.ts b/spec/unit/search/search-return-all-keys.spec.ts deleted file mode 100644 index 7f94135..0000000 --- a/spec/unit/search/search-return-all-keys.spec.ts +++ /dev/null @@ -1,117 +0,0 @@ -import { client } from '../helpers/mock-client' -import { Client } from "$lib/client" -import { EntityId } from '$lib/entity' -import { Search, RawSearch } from "$lib/search" - -import { simpleHashSchema } from "../helpers/test-entity-and-schema" -import { mockClientSearchToReturnNothing, mockClientSearchToReturnSingleKey, mockClientSearchToReturnMultipleKeys, - mockClientSearchToReturnPaginatedKeys, - SIMPLE_ENTITY_1, SIMPLE_ENTITY_2, SIMPLE_ENTITY_3, SIMPLE_ENTITY_4, SIMPLE_ENTITY_5 } from '../helpers/search-helpers' - - -type HashSearch = Search | RawSearch - -describe("Search", () => { - - describe.each([ - [ "FluentSearch", - new Search(simpleHashSchema, new Client()) ], - [ "RawSearch", - new RawSearch(simpleHashSchema, new Client()) ] - ])("%s", (_, search: HashSearch) => { - - describe("#returnAllKeys", () => { - let keys: string[] - let indexName = 'SimpleHashEntity:index', query = '*' - - describe("when querying no results", () => { - beforeEach(async () => { - mockClientSearchToReturnNothing() - keys = await search.return.allKeys() - }) - - it("askes the client for a single page of results", () => { - expect(client.search).toHaveBeenCalledTimes(1) - expect(client.search).toHaveBeenCalledWith(indexName, query, { - LIMIT: { from: 0, size: 10 }, - RETURN: [] - }) - }) - - it("returns no results", () => expect(keys).toHaveLength(0)) - }) - - describe("when querying a single result", () => { - beforeEach(async () => { - mockClientSearchToReturnSingleKey() - keys = await search.return.allKeys() - }) - - it("askes the client for a a single page of results", () => { - expect(client.search).toHaveBeenCalledTimes(1) - expect(client.search).toHaveBeenCalledWith(indexName, query, { - LIMIT: { from: 0, size: 10 }, - RETURN: [] - }) - }) - - it("returns the expected single result", () => { - expect(keys).toHaveLength(1) - expect(keys).toEqual(expect.arrayContaining([ - `SimpleHashEntity:${SIMPLE_ENTITY_1[EntityId]}` - ])) - }) - }) - - describe("when querying multiple results", () => { - beforeEach(async () => { - mockClientSearchToReturnMultipleKeys() - keys = await search.return.allKeys() - }) - - it("askes the client for a single page of results", () => { - expect(client.search).toHaveBeenCalledTimes(1) - expect(client.search).toHaveBeenCalledWith(indexName, query, { - LIMIT: { from: 0, size: 10 }, - RETURN: [] - }) - }) - - it("returns all the results", async () => { - expect(keys).toHaveLength(3) - expect(keys).toEqual(expect.arrayContaining([ - `SimpleHashEntity:${SIMPLE_ENTITY_1[EntityId]}`, - `SimpleHashEntity:${SIMPLE_ENTITY_2[EntityId]}`, - `SimpleHashEntity:${SIMPLE_ENTITY_3[EntityId]}` - ])) - }) - }) - - describe("when querying multiple results that cross the page boundry", () => { - beforeEach(async () => { - mockClientSearchToReturnPaginatedKeys() - keys = await search.return.allKeys({ pageSize: 2 }) - }) - - it("askes the client for multiple pages of results", () => { - expect(client.search).toHaveBeenCalledTimes(3) - expect(client.search).toHaveBeenCalledWith(indexName, query, { - LIMIT: { from: 0, size: 2 }, - RETURN: [] - }) - }) - - it("returns all the results", async () => { - expect(keys).toHaveLength(5) - expect(keys).toEqual(expect.arrayContaining([ - `SimpleHashEntity:${SIMPLE_ENTITY_1[EntityId]}`, - `SimpleHashEntity:${SIMPLE_ENTITY_2[EntityId]}`, - `SimpleHashEntity:${SIMPLE_ENTITY_3[EntityId]}`, - `SimpleHashEntity:${SIMPLE_ENTITY_4[EntityId]}`, - `SimpleHashEntity:${SIMPLE_ENTITY_5[EntityId]}` - ])) - }) - }) - }) - }) -}) diff --git a/spec/unit/search/search-return-all.spec.ts b/spec/unit/search/search-return-all.spec.ts deleted file mode 100644 index 1874a32..0000000 --- a/spec/unit/search/search-return-all.spec.ts +++ /dev/null @@ -1,217 +0,0 @@ -import { client } from '../helpers/mock-client' -import { Client } from "$lib/client" -import { Search, RawSearch } from "$lib/search" - -import { simpleHashSchema, simpleJsonSchema } from "../helpers/test-entity-and-schema" -import { mockClientSearchToReturnNothing, - mockClientSearchToReturnSingleHash, mockClientSearchToReturnSingleJsonString, - mockClientSearchToReturnMultipleHashes, mockClientSearchToReturnMultipleJsonStrings, - mockClientSearchToReturnPaginatedHashes, mockClientSearchToReturnPaginatedJsonStrings, - SIMPLE_ENTITY_1, SIMPLE_ENTITY_2, SIMPLE_ENTITY_3, SIMPLE_ENTITY_4, SIMPLE_ENTITY_5 } from '../helpers/search-helpers' - - -type HashSearch = Search | RawSearch -type JsonSearch = Search | RawSearch - -describe("Search", () => { - - describe.each([ - [ "FluentSearch", - new Search(simpleHashSchema, new Client()), - new Search(simpleJsonSchema, new Client()) ], - [ "RawSearch", - new RawSearch(simpleHashSchema, new Client()), - new RawSearch(simpleJsonSchema, new Client()) ] - ])("%s", (_, hashSearch: HashSearch, jsonSearch: JsonSearch) => { - - describe("#returnAll", () => { - describe("when running against hashes", () => { - let entities: object[] - let indexName = 'SimpleHashEntity:index', query = '*' - - describe("when querying no results", () => { - beforeEach(async () => { - mockClientSearchToReturnNothing() - entities = await hashSearch.return.all() - }) - - it("askes the client for a single page of results", () => { - expect(client.search).toHaveBeenCalledTimes(1) - expect(client.search).toHaveBeenCalledWith(indexName, query, { - LIMIT: { from: 0, size: 10 } }) - }) - - it("returns no results", () => expect(entities).toHaveLength(0)) - }) - - describe("when querying a single result", () => { - beforeEach(async () => { - mockClientSearchToReturnSingleHash() - entities = await hashSearch.return.all() - }) - - it("askes the client for a a single page of results", () => { - expect(client.search).toHaveBeenCalledTimes(1) - expect(client.search).toHaveBeenCalledWith(indexName, query, { - LIMIT: { from: 0, size: 10 } }) - }) - - it("returns the expected single result", () => { - expect(entities).toHaveLength(1) - expect(entities).toEqual(expect.arrayContaining([ - expect.objectContaining(SIMPLE_ENTITY_1) - ])) - }) - }) - - describe("when querying multiple results", () => { - beforeEach(async () => { - mockClientSearchToReturnMultipleHashes() - entities = await hashSearch.return.all() - }) - - it("askes the client for a single page of results", () => { - expect(client.search).toHaveBeenCalledTimes(1) - expect(client.search).toHaveBeenCalledWith(indexName, query, { - LIMIT: { from: 0, size: 10 } }) - }) - - it("returns all the results", async () => { - expect(entities).toHaveLength(3) - expect(entities).toEqual(expect.arrayContaining([ - expect.objectContaining(SIMPLE_ENTITY_1), - expect.objectContaining(SIMPLE_ENTITY_2), - expect.objectContaining(SIMPLE_ENTITY_3) - ])) - }) - }) - - describe("when querying multiple results that cross the page boundry", () => { - beforeEach(async () => { - mockClientSearchToReturnPaginatedHashes() - entities = await hashSearch.return.all({ pageSize: 2 }) - }) - - it("askes the client for multiple pages of results", () => { - expect(client.search).toHaveBeenCalledTimes(3) - expect(client.search).toHaveBeenCalledWith(indexName, query, { - LIMIT: { from: 0, size: 2 } }) - }) - - it("returns all the results", async () => { - expect(entities).toHaveLength(5) - expect(entities).toEqual(expect.arrayContaining([ - expect.objectContaining(SIMPLE_ENTITY_1), - expect.objectContaining(SIMPLE_ENTITY_2), - expect.objectContaining(SIMPLE_ENTITY_3), - expect.objectContaining(SIMPLE_ENTITY_4), - expect.objectContaining(SIMPLE_ENTITY_5) - ])) - }) - }) - }) - - describe("when running against JSON objects", () => { - let entities: object[] - let indexName = 'SimpleJsonEntity:index', query = '*' - - describe("when querying no results", () => { - beforeEach(async () => { - mockClientSearchToReturnNothing() - entities = await jsonSearch.return.all() - }) - - it("askes the client for a single page of results", () => { - expect(client.search).toHaveBeenCalledTimes(1) - expect(client.search).toHaveBeenCalledWith(indexName, query, { - LIMIT: { from: 0, size: 10 }, - RETURN: '$' - }) - }) - - it("returns no results", () => expect(entities).toHaveLength(0)) - }) - - describe("when querying a single result", () => { - beforeEach(async () => { - mockClientSearchToReturnSingleJsonString() - entities = await jsonSearch.return.all() - }) - - it("askes the client for a single page of results", () => { - expect(client.search).toHaveBeenCalledTimes(1) - expect(client.search).toHaveBeenCalledWith(indexName, query, { - LIMIT: { from: 0, size: 10 }, - RETURN: '$' - }) - }) - - it("returns the expected single result", () => { - expect(entities).toHaveLength(1) - expect(entities).toEqual(expect.arrayContaining([ - expect.objectContaining(SIMPLE_ENTITY_1) - ])) - }) - }) - - describe("when querying multiple results", () => { - beforeEach(async () => { - mockClientSearchToReturnMultipleJsonStrings() - entities = await jsonSearch.return.all() - }) - - it("askes the client for a single page of results", () => { - expect(client.search).toHaveBeenCalledTimes(1) - expect(client.search).toHaveBeenCalledWith(indexName, query, { - LIMIT: { from: 0, size: 10 }, - RETURN: '$' - }) - }) - - it("returns all the results", async () => { - expect(entities).toHaveLength(3) - expect(entities).toEqual(expect.arrayContaining([ - expect.objectContaining(SIMPLE_ENTITY_1), - expect.objectContaining(SIMPLE_ENTITY_2), - expect.objectContaining(SIMPLE_ENTITY_3) - ])) - }) - }) - - describe("when querying multiple results that cross the page boundry", () => { - beforeEach(async () => { - mockClientSearchToReturnPaginatedJsonStrings() - entities = await jsonSearch.return.all({ pageSize: 2 }) - }) - - it("askes the client for a multiple pages of results", () => { - expect(client.search).toHaveBeenCalledTimes(3) - expect(client.search).toHaveBeenCalledWith(indexName, query, { - LIMIT: { from: 0, size: 2 }, - RETURN: '$' - }) - expect(client.search).toHaveBeenCalledWith(indexName, query, { - LIMIT: { from: 2, size: 2 }, - RETURN: '$' - }) - expect(client.search).toHaveBeenCalledWith(indexName, query, { - LIMIT: { from: 4, size: 2 }, - RETURN: '$' - }) - }) - - it("returns all the results", async () => { - expect(entities).toHaveLength(5) - expect(entities).toEqual(expect.arrayContaining([ - expect.objectContaining(SIMPLE_ENTITY_1), - expect.objectContaining(SIMPLE_ENTITY_2), - expect.objectContaining(SIMPLE_ENTITY_3), - expect.objectContaining(SIMPLE_ENTITY_4), - expect.objectContaining(SIMPLE_ENTITY_5) - ])) - }) - }) - }) - }) - }) -}) diff --git a/spec/unit/search/search-return-count.spec.ts b/spec/unit/search/search-return-count.spec.ts deleted file mode 100644 index ea27d12..0000000 --- a/spec/unit/search/search-return-count.spec.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { client } from '../helpers/mock-client' -import { Client } from "$lib/client" -import { Search, RawSearch } from "$lib/search" - -import { simpleHashSchema, simpleJsonSchema } from "../helpers/test-entity-and-schema" -import { mockClientSearchToReturnCountOf } from "../helpers/search-helpers" - - -type HashSearch = Search | RawSearch -type JsonSearch = Search | RawSearch - -describe("Search", () => { - - describe.each([ - [ "FluentSearch", - new Search(simpleHashSchema, new Client()), - new Search(simpleJsonSchema, new Client()) ], - [ "RawSearch", - new RawSearch(simpleHashSchema, new Client()), - new RawSearch(simpleJsonSchema, new Client()) ] - ])("%s", (_, hashSearch: HashSearch, jsonSearch: JsonSearch) => { - - let actualCount: number - - describe("#returnCount", () => { - let query = '*', offset = 0, count = 0 - - describe("when counting results from hashes", () => { - - beforeEach(async () => { - mockClientSearchToReturnCountOf(3) - actualCount = await hashSearch.return.count() - }) - - it("askes the client for results", () => { - expect(client.search).toHaveBeenCalledTimes(1) - expect(client.search).toHaveBeenCalledWith('SimpleHashEntity:index', query, { - LIMIT: { from: offset, size: count } }) - }) - - it("returns the expected count", () => expect(actualCount).toBe(3)) - }) - - describe("when running against JSON objects", () => { - beforeEach(async () => { - mockClientSearchToReturnCountOf(3) - actualCount = await jsonSearch.return.count() - }) - - it("askes the client for results", () => { - expect(client.search).toHaveBeenCalledTimes(1) - expect(client.search).toHaveBeenCalledWith('SimpleJsonEntity:index', query, { - LIMIT: { from: offset, size: count }, - RETURN: '$' - }) - }) - - it("returns the expected count", () => expect(actualCount).toBe(3)) - }) - }) - }) -}) diff --git a/spec/unit/search/search-return-first-id.spec.ts b/spec/unit/search/search-return-first-id.spec.ts deleted file mode 100644 index 9623181..0000000 --- a/spec/unit/search/search-return-first-id.spec.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { client } from '../helpers/mock-client' -import { Client } from "$lib/client" -import { EntityId } from '$lib/entity' -import { Search, RawSearch } from "$lib/search" - -import { simpleHashSchema,} from "../helpers/test-entity-and-schema" -import { mockClientSearchToReturnNothing, mockClientSearchToReturnSingleKey, - SIMPLE_ENTITY_1 } from '../helpers/search-helpers' - - -type HashSearch = Search | RawSearch - -describe.each([ - [ "FluentSearch", - new Search(simpleHashSchema, new Client()) ], - [ "RawSearch", - new RawSearch(simpleHashSchema, new Client()) ] -])("%s", (_, search: HashSearch) => { - - describe("#returnFirstId", () => { - let id: string | null - let indexName = 'SimpleHashEntity:index', query = '*' - - describe("when querying no results", () => { - beforeEach( async () => { - mockClientSearchToReturnNothing() - id = await search.return.firstId() - }) - - it("asks the client for the first result of a given repository", () => { - expect(client.search).toHaveBeenCalledTimes(1) - expect(client.search).toHaveBeenCalledWith(indexName, query, { - LIMIT: { from: 0, size: 1 }, - RETURN: [] - }) - }) - - it("return no result", () => expect(id).toBe(null)) - }) - - describe("when getting a result", () => { - beforeEach(async () => { - mockClientSearchToReturnSingleKey() - id = await search.return.firstId() - }) - - it("asks the client for the first result of a given repository", () => { - expect(client.search).toHaveBeenCalledTimes(1) - expect(client.search).toHaveBeenCalledWith(indexName, query, { - LIMIT: { from: 0, size: 1 }, - RETURN: [] - }) - }) - - it("returns the first result of a given repository", () => { - expect(id).toEqual(SIMPLE_ENTITY_1[EntityId]) - }) - }) - }) -}) diff --git a/spec/unit/search/search-return-first-key.spec.ts b/spec/unit/search/search-return-first-key.spec.ts deleted file mode 100644 index 76cc2f2..0000000 --- a/spec/unit/search/search-return-first-key.spec.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { client } from '../helpers/mock-client' -import { Client } from "$lib/client" -import { EntityId } from '$lib/entity' -import { Search, RawSearch } from "$lib/search" - -import { simpleHashSchema } from "../helpers/test-entity-and-schema" -import { mockClientSearchToReturnNothing, mockClientSearchToReturnSingleKey, - SIMPLE_ENTITY_1 } from '../helpers/search-helpers' - - -type HashSearch = Search | RawSearch - -describe.each([ - [ "FluentSearch", - new Search(simpleHashSchema, new Client()) ], - [ "RawSearch", - new RawSearch(simpleHashSchema, new Client()) ] -])("%s", (_, search: HashSearch) => { - - describe("#returnFirstKey", () => { - let id: string | null - let indexName = 'SimpleHashEntity:index', query = '*' - - describe("when querying no results", () => { - beforeEach( async () => { - mockClientSearchToReturnNothing() - id = await search.return.firstKey() - }) - - it("asks the client for the first result of a given repository", () => { - expect(client.search).toHaveBeenCalledTimes(1) - expect(client.search).toHaveBeenCalledWith(indexName, query, { - LIMIT: { from: 0, size: 1 }, - RETURN: [] - }) - }) - - it("return no result", () => expect(id).toBe(null)) - }) - - describe("when getting a result", () => { - beforeEach(async () => { - mockClientSearchToReturnSingleKey() - id = await search.return.firstKey() - }) - - it("asks the client for the first result of a given repository", () => { - expect(client.search).toHaveBeenCalledTimes(1) - expect(client.search).toHaveBeenCalledWith(indexName, query, { - LIMIT: { from: 0, size: 1 }, - RETURN: [] - }) - }) - - it("returns the first result of a given repository", () => { - expect(id).toEqual(`SimpleHashEntity:${SIMPLE_ENTITY_1[EntityId]}`) - }) - }) - }) -}) diff --git a/spec/unit/search/search-return-first.spec.ts b/spec/unit/search/search-return-first.spec.ts deleted file mode 100644 index 4fa68fc..0000000 --- a/spec/unit/search/search-return-first.spec.ts +++ /dev/null @@ -1,110 +0,0 @@ -import { client } from '../helpers/mock-client' -import { Client } from "$lib/client" -import { Entity, EntityId } from '$lib/entity' -import { Search, RawSearch } from "$lib/search" - -import { simpleHashSchema, simpleJsonSchema } from "../helpers/test-entity-and-schema" -import { mockClientSearchToReturnNothing, mockClientSearchToReturnSingleHash, - mockClientSearchToReturnSingleJsonString, SIMPLE_ENTITY_1 } from '../helpers/search-helpers' - - -type HashSearch = Search | RawSearch -type JsonSearch = Search | RawSearch - - -describe.each([ - [ "FluentSearch", - new Search(simpleHashSchema, new Client()), - new Search(simpleJsonSchema, new Client()) ], - [ "RawSearch", - new RawSearch(simpleHashSchema, new Client()), - new RawSearch(simpleJsonSchema, new Client()) ] -])("%s", (_, hashSearch: HashSearch, jsonSearch: JsonSearch) => { - - describe("#returnFirst", () => { - describe("when running against hashes", () => { - let entity: Entity | null - let indexName = 'SimpleHashEntity:index', query = '*' - - describe("when querying no results", () => { - beforeEach( async () => { - mockClientSearchToReturnNothing() - entity = await hashSearch.return.first() - }) - - it("asks the client for the first result of a given repository", () => { - expect(client.search).toHaveBeenCalledTimes(1) - expect(client.search).toHaveBeenCalledWith(indexName, query, { - LIMIT: { from: 0, size: 1 } }) - }) - - it("return no result", () => expect(entity).toBe(null)) - }) - - describe("when getting a result", () => { - beforeEach(async () => { - mockClientSearchToReturnSingleHash() - entity = await hashSearch.return.first() - }) - - it("asks the client for the first result of a given repository", () => { - expect(client.search).toHaveBeenCalledTimes(1) - expect(client.search).toHaveBeenCalledWith(indexName, query, { - LIMIT: { from: 0, size: 1 } }) - }) - - it("returns the first result of a given repository", () => { - expect(entity?.aBoolean).toEqual(SIMPLE_ENTITY_1.aBoolean) - expect(entity?.aNumber).toEqual(SIMPLE_ENTITY_1.aNumber) - expect(entity?.aString).toEqual(SIMPLE_ENTITY_1.aString) - expect(entity ? entity[EntityId] : null).toEqual(SIMPLE_ENTITY_1[EntityId]) - }) - }) - }) - - describe("when running against JSON Objects", () => { - let entity: Entity | null - let indexName = 'SimpleJsonEntity:index', query = '*' - - describe("when querying no results", () => { - beforeEach( async () => { - mockClientSearchToReturnNothing() - entity = await jsonSearch.return.first() - }) - - it("asks the client for the first result of a given repository", () => { - expect(client.search).toHaveBeenCalledTimes(1) - expect(client.search).toHaveBeenCalledWith(indexName, query, { - LIMIT: { from: 0, size: 1 }, - RETURN: '$' - }) - }) - - it("return no result", () => expect(entity).toBe(null)) - }) - - describe("when getting a result", () => { - beforeEach(async () => { - mockClientSearchToReturnSingleJsonString() - entity = await jsonSearch.return.first() - }) - - it("asks the client for the first result of a given repository", () => { - expect(client.search).toHaveBeenCalledTimes(1) - expect(client.search).toHaveBeenCalledWith(indexName, query, { - LIMIT: { from: 0, size: 1 }, - RETURN: '$' - }) - }) - - it("returns the first result of a given repository", () => { - expect(entity).toBeDefined() - expect(entity?.aBoolean).toEqual(SIMPLE_ENTITY_1.aBoolean) - expect(entity?.aNumber).toEqual(SIMPLE_ENTITY_1.aNumber) - expect(entity?.aString).toEqual(SIMPLE_ENTITY_1.aString) - expect(entity ? entity[EntityId] : null).toEqual(SIMPLE_ENTITY_1[EntityId]) - }) - }) - }) - }) -}) diff --git a/spec/unit/search/search-return-max-id.spec.ts b/spec/unit/search/search-return-max-id.spec.ts deleted file mode 100644 index 5f75be6..0000000 --- a/spec/unit/search/search-return-max-id.spec.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { client } from '../helpers/mock-client' -import { Client } from "$lib/client" -import { EntityId } from '$lib/entity' -import { Search, RawSearch } from "$lib/search" - -import { simpleHashSchema } from "../helpers/test-entity-and-schema" -import { mockClientSearchToReturnNothing, mockClientSearchToReturnSingleKey, SIMPLE_ENTITY_1 } from '../helpers/search-helpers' - -console.warn = vi.fn() - - -type HashSearch = Search | RawSearch - - -describe.each([ - [ "FluentSearch", - new Search(simpleHashSchema, new Client()) ], - [ "RawSearch", - new RawSearch(simpleHashSchema, new Client()) ] -])("%s", (_, search: HashSearch) => { - - describe("#returnMaxId", () => { - let id: string | null - let indexName = 'SimpleHashEntity:index', query = '*' - - describe("when querying no results", () => { - beforeEach( async () => { - mockClientSearchToReturnNothing() - id = await search.return.maxId('aNumber') - }) - - it("asks the client for the first result of a given repository", () => { - expect(client.search).toHaveBeenCalledTimes(1) - expect(client.search).toHaveBeenCalledWith(indexName, query, { - LIMIT: { from: 0, size: 1 }, - SORTBY: { BY: 'aNumber', DIRECTION: 'DESC' }, - RETURN: [] - }) - }) - - it("return no result", () => expect(id).toBe(null)) - }) - - describe("when getting a result", () => { - beforeEach(async () => { - mockClientSearchToReturnSingleKey() - id = await search.return.maxId('aNumber') - }) - - it("asks the client for the first result of a given repository", () => { - expect(client.search).toHaveBeenCalledTimes(1) - expect(client.search).toHaveBeenCalledWith(indexName, query, { - LIMIT: { from: 0, size: 1 }, - SORTBY: { BY: 'aNumber', DIRECTION: 'DESC' }, - RETURN: [] - }) - }) - - it("returns the first result of a given repository", () => { - expect(id).toEqual(SIMPLE_ENTITY_1[EntityId]) - }) - }) - }) -}) diff --git a/spec/unit/search/search-return-max-key.spec.ts b/spec/unit/search/search-return-max-key.spec.ts deleted file mode 100644 index 57a9545..0000000 --- a/spec/unit/search/search-return-max-key.spec.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { client } from '../helpers/mock-client' -import { Client } from "$lib/client" -import { EntityId } from '$lib/entity' -import { Search, RawSearch } from "$lib/search" - -import { simpleHashSchema } from "../helpers/test-entity-and-schema" -import { mockClientSearchToReturnNothing, mockClientSearchToReturnSingleKey, - SIMPLE_ENTITY_1 } from '../helpers/search-helpers' - -console.warn = vi.fn() - - -type HashSearch = Search | RawSearch - - -describe.each([ - [ "FluentSearch", - new Search(simpleHashSchema, new Client()) ], - [ "RawSearch", - new RawSearch(simpleHashSchema, new Client()) ] -])("%s", (_, search: HashSearch) => { - - describe("#returnMaxKey", () => { - let key: string | null - let indexName = 'SimpleHashEntity:index', query = '*' - - describe("when querying no results", () => { - beforeEach( async () => { - mockClientSearchToReturnNothing() - key = await search.return.maxKey('aNumber') - }) - - it("asks the client for the first result of a given repository", () => { - expect(client.search).toHaveBeenCalledTimes(1) - expect(client.search).toHaveBeenCalledWith(indexName, query, { - LIMIT: { from: 0, size: 1 }, - SORTBY: { BY: 'aNumber', DIRECTION: 'DESC' }, - RETURN: [] - }) - }) - - it("return no result", () => expect(key).toBe(null)) - }) - - describe("when getting a result", () => { - beforeEach(async () => { - mockClientSearchToReturnSingleKey() - key = await search.return.maxKey('aNumber') - }) - - it("asks the client for the first result of a given repository", () => { - expect(client.search).toHaveBeenCalledTimes(1) - expect(client.search).toHaveBeenCalledWith(indexName, query, { - LIMIT: { from: 0, size: 1 }, - SORTBY: { BY: 'aNumber', DIRECTION: 'DESC' }, - RETURN: [] - }) - }) - - it("returns the first result of a given repository", () => { - expect(key).toEqual(`SimpleHashEntity:${SIMPLE_ENTITY_1[EntityId]}`) - }) - }) - }) -}) diff --git a/spec/unit/search/search-return-max.spec.ts b/spec/unit/search/search-return-max.spec.ts deleted file mode 100644 index db44946..0000000 --- a/spec/unit/search/search-return-max.spec.ts +++ /dev/null @@ -1,115 +0,0 @@ -import { client } from '../helpers/mock-client' -import { Client } from "$lib/client" -import { Entity, EntityId } from '$lib/entity' -import { Search, RawSearch } from "$lib/search" - -import { simpleHashSchema, simpleJsonSchema } from "../helpers/test-entity-and-schema" -import { mockClientSearchToReturnNothing, mockClientSearchToReturnSingleHash, - mockClientSearchToReturnSingleJsonString, SIMPLE_ENTITY_1 } from '../helpers/search-helpers' - -console.warn = vi.fn() - - -type HashSearch = Search | RawSearch -type JsonSearch = Search | RawSearch - - -describe.each([ - [ "FluentSearch", - new Search(simpleHashSchema, new Client()), - new Search(simpleJsonSchema, new Client()) ], - [ "RawSearch", - new RawSearch(simpleHashSchema, new Client()), - new RawSearch(simpleJsonSchema, new Client()) ] -])("%s", (_, hashSearch: HashSearch, jsonSearch: JsonSearch) => { - - describe("#returnMin", () => { - describe("when running against hashes", () => { - let entity: Entity | null - let indexName = 'SimpleHashEntity:index', query = '*' - - describe("when querying no results", () => { - beforeEach( async () => { - mockClientSearchToReturnNothing() - entity = await hashSearch.return.max('aNumber') - }) - - it("asks the client for the first result of a given repository", () => { - expect(client.search).toHaveBeenCalledTimes(1) - expect(client.search).toHaveBeenCalledWith(indexName, query, { - LIMIT: { from: 0, size: 1 }, - SORTBY: { BY: 'aNumber', DIRECTION: 'DESC' } }) - }) - - it("return no result", () => expect(entity).toBe(null)) - }) - - describe("when getting a result", () => { - beforeEach(async () => { - mockClientSearchToReturnSingleHash() - entity = await hashSearch.return.max('aNumber') - }) - - it("asks the client for the first result of a given repository", () => { - expect(client.search).toHaveBeenCalledTimes(1) - expect(client.search).toHaveBeenCalledWith(indexName, query, { - LIMIT: { from: 0, size: 1 }, - SORTBY: { BY: 'aNumber', DIRECTION: 'DESC' } }) - }) - - it("returns the first result of a given repository", () => { - expect(entity?.aBoolean).toEqual(SIMPLE_ENTITY_1.aBoolean) - expect(entity?.aNumber).toEqual(SIMPLE_ENTITY_1.aNumber) - expect(entity?.aString).toEqual(SIMPLE_ENTITY_1.aString) - expect(entity ? entity[EntityId] : null).toEqual(SIMPLE_ENTITY_1[EntityId]) - }) - }) - }) - - describe("when running against JSON Objects", () => { - let entity: Entity | null - let indexName = 'SimpleJsonEntity:index', query = '*' - - describe("when querying no results", () => { - beforeEach( async () => { - mockClientSearchToReturnNothing() - entity = await jsonSearch.return.max('aNumber') - }) - - it("asks the client for the first result of a given repository", () => { - expect(client.search).toHaveBeenCalledTimes(1) - expect(client.search).toHaveBeenCalledWith(indexName, query, { - LIMIT: { from: 0, size: 1 }, - SORTBY: { BY: 'aNumber', DIRECTION: 'DESC' }, - RETURN: '$' - }) - }) - - it("return no result", () => expect(entity).toBe(null)) - }) - - describe("when getting a result", () => { - beforeEach(async () => { - mockClientSearchToReturnSingleJsonString() - entity = await jsonSearch.return.max('aNumber') - }) - - it("asks the client for the first result of a given repository", () => { - expect(client.search).toHaveBeenCalledTimes(1) - expect(client.search).toHaveBeenCalledWith(indexName, query, { - LIMIT: { from: 0, size: 1 }, - SORTBY: { BY: 'aNumber', DIRECTION: 'DESC' }, - RETURN: '$' - }) - }) - - it("returns the first result of a given repository", () => { - expect(entity?.aBoolean).toEqual(SIMPLE_ENTITY_1.aBoolean) - expect(entity?.aNumber).toEqual(SIMPLE_ENTITY_1.aNumber) - expect(entity?.aString).toEqual(SIMPLE_ENTITY_1.aString) - expect(entity ? entity[EntityId] : null).toEqual(SIMPLE_ENTITY_1[EntityId]) - }) - }) - }) - }) -}) diff --git a/spec/unit/search/search-return-min-id.spec.ts b/spec/unit/search/search-return-min-id.spec.ts deleted file mode 100644 index 169a640..0000000 --- a/spec/unit/search/search-return-min-id.spec.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { client } from '../helpers/mock-client' -import { Client } from "$lib/client" -import { EntityId } from '$lib/entity' -import { Search, RawSearch } from "$lib/search" - -import { simpleHashSchema } from "../helpers/test-entity-and-schema" -import { mockClientSearchToReturnNothing, mockClientSearchToReturnSingleKey, - SIMPLE_ENTITY_1 } from '../helpers/search-helpers' - -console.warn = vi.fn() - - -type HashSearch = Search | RawSearch - -describe.each([ - [ "FluentSearch", - new Search(simpleHashSchema, new Client()) ], - [ "RawSearch", - new RawSearch(simpleHashSchema, new Client()) ] -])("%s", (_, search: HashSearch) => { - - describe("#returnMinId", () => { - let id: string | null - let indexName = 'SimpleHashEntity:index', query = '*' - - describe("when querying no results", () => { - beforeEach( async () => { - mockClientSearchToReturnNothing() - id = await search.return.minId('aNumber') - }) - - it("asks the client for the first result of a given repository", () => { - expect(client.search).toHaveBeenCalledTimes(1) - expect(client.search).toHaveBeenCalledWith(indexName, query, { - LIMIT: { from: 0, size: 1 }, - SORTBY: { BY: 'aNumber', DIRECTION: 'ASC' }, - RETURN: [] - }) - }) - - it("return no result", () => expect(id).toBe(null)) - }) - - describe("when getting a result", () => { - beforeEach(async () => { - mockClientSearchToReturnSingleKey() - id = await search.return.minId('aNumber') - }) - - it("asks the client for the first result of a given repository", () => { - expect(client.search).toHaveBeenCalledTimes(1) - expect(client.search).toHaveBeenCalledWith(indexName, query, { - LIMIT: { from: 0, size: 1 }, - SORTBY: { BY: 'aNumber', DIRECTION: 'ASC' }, - RETURN: [] - }) - }) - - it("returns the first result of a given repository", () => { - expect(id).toEqual(SIMPLE_ENTITY_1[EntityId]) - }) - }) - }) -}) diff --git a/spec/unit/search/search-return-min-key.spec.ts b/spec/unit/search/search-return-min-key.spec.ts deleted file mode 100644 index 510b960..0000000 --- a/spec/unit/search/search-return-min-key.spec.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { client } from '../helpers/mock-client' -import { Client } from "$lib/client" -import { EntityId } from '$lib/entity' -import { Search, RawSearch } from "$lib/search" - -import { simpleHashSchema } from "../helpers/test-entity-and-schema" -import { mockClientSearchToReturnNothing, mockClientSearchToReturnSingleKey, - SIMPLE_ENTITY_1 } from '../helpers/search-helpers' - -console.warn = vi.fn() - - -type HashSearch = Search | RawSearch - -describe.each([ - [ "FluentSearch", - new Search(simpleHashSchema, new Client()) ], - [ "RawSearch", - new RawSearch(simpleHashSchema, new Client()) ] -])("%s", (_, search: HashSearch) => { - - describe("#returnMinKey", () => { - let key: string | null - let indexName = 'SimpleHashEntity:index', query = '*' - - describe("when querying no results", () => { - beforeEach( async () => { - mockClientSearchToReturnNothing() - key = await search.return.minKey('aNumber') - }) - - it("asks the client for the first result of a given repository", () => { - expect(client.search).toHaveBeenCalledTimes(1) - expect(client.search).toHaveBeenCalledWith(indexName, query, { - LIMIT: { from: 0, size: 1 }, - SORTBY: { BY: 'aNumber', DIRECTION: 'ASC' }, - RETURN: [] - }) - }) - - it("return no result", () => expect(key).toBe(null)) - }) - - describe("when getting a result", () => { - beforeEach(async () => { - mockClientSearchToReturnSingleKey() - key = await search.return.minKey('aNumber') - }) - - it("asks the client for the first result of a given repository", () => { - expect(client.search).toHaveBeenCalledTimes(1) - expect(client.search).toHaveBeenCalledWith(indexName, query, { - LIMIT: { from: 0, size: 1 }, - SORTBY: { BY: 'aNumber', DIRECTION: 'ASC' }, - RETURN: [] - }) - }) - - it("returns the first result of a given repository", () => { - expect(key).toEqual(`SimpleHashEntity:${SIMPLE_ENTITY_1[EntityId]}`) - }) - }) - }) -}) diff --git a/spec/unit/search/search-return-min.spec.ts b/spec/unit/search/search-return-min.spec.ts deleted file mode 100644 index cf77adf..0000000 --- a/spec/unit/search/search-return-min.spec.ts +++ /dev/null @@ -1,116 +0,0 @@ -import { client } from '../helpers/mock-client' - -import { Client } from "$lib/client" -import { Entity, EntityId } from '$lib/entity' -import { Search, RawSearch } from "$lib/search" - -import { simpleHashSchema, simpleJsonSchema } from "../helpers/test-entity-and-schema" -import { mockClientSearchToReturnNothing, mockClientSearchToReturnSingleHash, - mockClientSearchToReturnSingleJsonString, SIMPLE_ENTITY_1 } from '../helpers/search-helpers' - -console.warn = vi.fn() - - -type HashSearch = Search | RawSearch -type JsonSearch = Search | RawSearch - -describe.each([ - [ "FluentSearch", - new Search(simpleHashSchema, new Client()), - new Search(simpleJsonSchema, new Client()) ], - [ "RawSearch", - new RawSearch(simpleHashSchema, new Client()), - new RawSearch(simpleJsonSchema, new Client()) ] -])("%s", (_, hashSearch: HashSearch, jsonSearch: JsonSearch) => { - - describe("#returnMin", () => { - - let entity: Entity | null - - describe("when running against hashes", () => { - let indexName = 'SimpleHashEntity:index', query = '*' - - describe("when querying no results", () => { - beforeEach( async () => { - mockClientSearchToReturnNothing() - entity = await hashSearch.return.min('aNumber') - }) - - it("asks the client for the first result of a given repository", () => { - expect(client.search).toHaveBeenCalledTimes(1) - expect(client.search).toHaveBeenCalledWith(indexName, query, { - LIMIT: { from: 0, size: 1 }, - SORTBY: { BY: 'aNumber', DIRECTION: 'ASC' } }) - }) - - it("return no result", () => expect(entity).toBe(null)) - }) - - describe("when getting a result", () => { - beforeEach(async () => { - mockClientSearchToReturnSingleHash() - entity = await hashSearch.return.min('aNumber') - }) - - it("asks the client for the first result of a given repository", () => { - expect(client.search).toHaveBeenCalledTimes(1) - expect(client.search).toHaveBeenCalledWith(indexName, query, { - LIMIT: { from: 0, size: 1 }, - SORTBY: { BY: 'aNumber', DIRECTION: 'ASC' } }) - }) - - it("returns the first result of a given repository", () => { - expect(entity?.aBoolean).toEqual(SIMPLE_ENTITY_1.aBoolean) - expect(entity?.aNumber).toEqual(SIMPLE_ENTITY_1.aNumber) - expect(entity?.aString).toEqual(SIMPLE_ENTITY_1.aString) - expect(entity ? entity[EntityId] : null).toEqual(SIMPLE_ENTITY_1[EntityId]) - }) - }) - }) - - describe("when running against JSON Objects", () => { - let indexName = 'SimpleJsonEntity:index', query = '*' - - describe("when querying no results", () => { - beforeEach( async () => { - mockClientSearchToReturnNothing() - entity = await jsonSearch.return.min('aNumber') - }) - - it("asks the client for the first result of a given repository", () => { - expect(client.search).toHaveBeenCalledTimes(1) - expect(client.search).toHaveBeenCalledWith(indexName, query, { - LIMIT: { from: 0, size: 1 }, - SORTBY: { BY: 'aNumber', DIRECTION: 'ASC' }, - RETURN: '$' - }) - }) - - it("return no result", () => expect(entity).toBe(null)) - }) - - describe("when getting a result", () => { - beforeEach(async () => { - mockClientSearchToReturnSingleJsonString() - entity = await jsonSearch.return.min('aNumber') - }) - - it("asks the client for the first result of a given repository", () => { - expect(client.search).toHaveBeenCalledTimes(1) - expect(client.search).toHaveBeenCalledWith(indexName, query, { - LIMIT: { from: 0, size: 1 }, - SORTBY: { BY: 'aNumber', DIRECTION: 'ASC' }, - RETURN: '$' - }) - }) - - it("returns the first result of a given repository", () => { - expect(entity?.aBoolean).toEqual(SIMPLE_ENTITY_1.aBoolean) - expect(entity?.aNumber).toEqual(SIMPLE_ENTITY_1.aNumber) - expect(entity?.aString).toEqual(SIMPLE_ENTITY_1.aString) - expect(entity ? entity[EntityId] : null).toEqual(SIMPLE_ENTITY_1[EntityId]) - }) - }) - }) - }) -}) diff --git a/spec/unit/search/search-return-page-of-ids.spec.ts b/spec/unit/search/search-return-page-of-ids.spec.ts deleted file mode 100644 index d03c8b1..0000000 --- a/spec/unit/search/search-return-page-of-ids.spec.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { client } from '../helpers/mock-client' -import { Client } from "$lib/client" -import { EntityId } from '$lib/entity' -import { Search, RawSearch } from "$lib/search" - -import { simpleHashSchema } from "../helpers/test-entity-and-schema" -import { mockClientSearchToReturnNothing, - mockClientSearchToReturnSingleKey, mockClientSearchToReturnMultipleKeys, - SIMPLE_ENTITY_1, SIMPLE_ENTITY_2, SIMPLE_ENTITY_3 } from '../helpers/search-helpers' - - -type HashSearch = Search | RawSearch - -describe.each([ - [ "FluentSearch", - new Search(simpleHashSchema, new Client()) ], - [ "RawSearch", - new RawSearch(simpleHashSchema, new Client()) ] -])("%s", (_, search: HashSearch) => { - - describe("#returnPageOfIds", () => { - - let keys: string[] - let indexName = 'SimpleHashEntity:index', query = '*' - - describe("when querying no results", () => { - beforeEach(async () => { - mockClientSearchToReturnNothing() - keys = await search.return.pageOfIds(0, 5) - }) - - it("askes the client for results", () => { - expect(client.search).toHaveBeenCalledTimes(1) - expect(client.search).toHaveBeenCalledWith(indexName, query, { - LIMIT: { from: 0, size: 5 }, - RETURN: [] - }) - }) - - it("returns no results", () => expect(keys).toHaveLength(0)) - }) - - describe("when querying a single result", () => { - beforeEach(async () => { - mockClientSearchToReturnSingleKey() - keys = await search.return.pageOfIds(0, 5) - }) - - it("askes the client for results", () => { - expect(client.search).toHaveBeenCalledTimes(1) - expect(client.search).toHaveBeenCalledWith(indexName, query, { - LIMIT: { from: 0, size: 5 }, - RETURN: [] - }) - }) - - it("returns the expected single result", () => { - expect(keys).toHaveLength(1) - expect(keys).toEqual(expect.arrayContaining([ - SIMPLE_ENTITY_1[EntityId] - ])) - }) - }) - - describe("when querying multiple results", () => { - beforeEach(async () => { - mockClientSearchToReturnMultipleKeys() - keys = await search.return.pageOfIds(0, 5) - }) - - it("askes the client for results", () => { - expect(client.search).toHaveBeenCalledTimes(1) - expect(client.search).toHaveBeenCalledWith(indexName, query, { - LIMIT: { from: 0, size: 5 }, - RETURN: [] - }) - }) - - it("returns all the results", async () => { - expect(keys).toHaveLength(3) - expect(keys).toEqual(expect.arrayContaining([ - SIMPLE_ENTITY_1[EntityId], - SIMPLE_ENTITY_2[EntityId], - SIMPLE_ENTITY_3[EntityId] - ])) - }) - }) - }) -}) diff --git a/spec/unit/search/search-return-page-of-keys.spec.ts b/spec/unit/search/search-return-page-of-keys.spec.ts deleted file mode 100644 index 981358c..0000000 --- a/spec/unit/search/search-return-page-of-keys.spec.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { client } from '../helpers/mock-client' -import { Client } from "$lib/client" -import { EntityId } from '$lib/entity' -import { Search, RawSearch } from "$lib/search" - -import { simpleHashSchema } from "../helpers/test-entity-and-schema" -import { mockClientSearchToReturnNothing, - mockClientSearchToReturnSingleKey, mockClientSearchToReturnMultipleKeys, - SIMPLE_ENTITY_1, SIMPLE_ENTITY_2, SIMPLE_ENTITY_3 } from '../helpers/search-helpers' - - -type HashSearch = Search | RawSearch - -describe.each([ - [ "FluentSearch", - new Search(simpleHashSchema, new Client()) ], - [ "RawSearch", - new RawSearch(simpleHashSchema, new Client()) ] -])("%s", (_, search: HashSearch) => { - - describe("#returnPageOfKeys", () => { - - let keys: string[] - let indexName = 'SimpleHashEntity:index', query = '*' - - describe("when querying no results", () => { - beforeEach(async () => { - mockClientSearchToReturnNothing() - keys = await search.return.pageOfKeys(0, 5) - }) - - it("askes the client for results", () => { - expect(client.search).toHaveBeenCalledTimes(1) - expect(client.search).toHaveBeenCalledWith(indexName, query, { - LIMIT: { from: 0, size: 5 }, - RETURN: [] - }) - }) - - it("returns no results", () => expect(keys).toHaveLength(0)) - }) - - describe("when querying a single result", () => { - beforeEach(async () => { - mockClientSearchToReturnSingleKey() - keys = await search.return.pageOfKeys(0, 5) - }) - - it("askes the client for results", () => { - expect(client.search).toHaveBeenCalledTimes(1) - expect(client.search).toHaveBeenCalledWith(indexName, query, { - LIMIT: { from: 0, size: 5 }, - RETURN: [] - }) - }) - - it("returns the expected single result", () => { - expect(keys).toHaveLength(1) - expect(keys).toEqual(expect.arrayContaining([ - `SimpleHashEntity:${SIMPLE_ENTITY_1[EntityId]}` - ])) - }) - }) - - describe("when querying multiple results", () => { - beforeEach(async () => { - mockClientSearchToReturnMultipleKeys() - keys = await search.return.pageOfKeys(0, 5) - }) - - it("askes the client for results", () => { - expect(client.search).toHaveBeenCalledTimes(1) - expect(client.search).toHaveBeenCalledWith(indexName, query, { - LIMIT: { from: 0, size: 5 }, - RETURN: [] - }) - }) - - it("returns all the results", async () => { - expect(keys).toHaveLength(3) - expect(keys).toEqual(expect.arrayContaining([ - `SimpleHashEntity:${SIMPLE_ENTITY_1[EntityId]}`, - `SimpleHashEntity:${SIMPLE_ENTITY_2[EntityId]}`, - `SimpleHashEntity:${SIMPLE_ENTITY_3[EntityId]}` - ])) - }) - }) - }) -}) diff --git a/spec/unit/search/search-return-page.spec.ts b/spec/unit/search/search-return-page.spec.ts deleted file mode 100644 index 7325554..0000000 --- a/spec/unit/search/search-return-page.spec.ts +++ /dev/null @@ -1,156 +0,0 @@ -import { client } from '../helpers/mock-client' -import { Client } from "$lib/client" -import { Search, RawSearch } from "$lib/search" - -import { simpleHashSchema, simpleJsonSchema } from "../helpers/test-entity-and-schema" -import { mockClientSearchToReturnNothing, - mockClientSearchToReturnSingleHash, mockClientSearchToReturnSingleJsonString, - mockClientSearchToReturnMultipleHashes, mockClientSearchToReturnMultipleJsonStrings, - SIMPLE_ENTITY_1, SIMPLE_ENTITY_2, SIMPLE_ENTITY_3 } from '../helpers/search-helpers' - - -type HashSearch = Search | RawSearch -type JsonSearch = Search | RawSearch - -describe.each([ - [ "FluentSearch", - new Search(simpleHashSchema, new Client()), - new Search(simpleJsonSchema, new Client()) ], - [ "RawSearch", - new RawSearch(simpleHashSchema, new Client()), - new RawSearch(simpleJsonSchema, new Client()) ] -])("%s", (_, hashSearch: HashSearch, jsonSearch: JsonSearch) => { - - describe("#returnPage", () => { - - describe("when running against hashes", () => { - let entities: object[] - let indexName = 'SimpleHashEntity:index', query = '*' - - describe("when querying no results", () => { - beforeEach(async () => { - mockClientSearchToReturnNothing() - entities = await hashSearch.return.page(0, 5) - }) - - it("askes the client for results", () => { - expect(client.search).toHaveBeenCalledTimes(1) - expect(client.search).toHaveBeenCalledWith(indexName, query, { - LIMIT: { from: 0, size: 5 } }) - }) - - it("returns no results", () => expect(entities).toHaveLength(0)) - }) - - describe("when querying a single result", () => { - beforeEach(async () => { - mockClientSearchToReturnSingleHash() - entities = await hashSearch.return.page(0, 5) - }) - - it("askes the client for results", () => { - expect(client.search).toHaveBeenCalledTimes(1) - expect(client.search).toHaveBeenCalledWith(indexName, query, { - LIMIT: { from: 0, size: 5 } }) - }) - - it("returns the expected single result", () => { - expect(entities).toHaveLength(1) - expect(entities).toEqual(expect.arrayContaining([ - expect.objectContaining(SIMPLE_ENTITY_1) - ])) - }) - }) - - describe("when querying multiple results", () => { - beforeEach(async () => { - mockClientSearchToReturnMultipleHashes() - entities = await hashSearch.return.page(0, 5) - }) - - it("askes the client for results", () => { - expect(client.search).toHaveBeenCalledTimes(1) - expect(client.search).toHaveBeenCalledWith(indexName, query, { - LIMIT: { from: 0, size: 5 } }) - }) - - it("returns all the results", async () => { - expect(entities).toHaveLength(3) - expect(entities).toEqual(expect.arrayContaining([ - expect.objectContaining(SIMPLE_ENTITY_1), - expect.objectContaining(SIMPLE_ENTITY_2), - expect.objectContaining(SIMPLE_ENTITY_3) - ])) - }) - }) - }) - - describe("when running against JSON objects", () => { - let entities: object[] - let indexName = 'SimpleJsonEntity:index', query = '*' - - describe("when querying no results", () => { - beforeEach(async () => { - mockClientSearchToReturnNothing() - entities = await jsonSearch.return.page(0, 5) - }) - - it("askes the client for results", () => { - expect(client.search).toHaveBeenCalledTimes(1) - expect(client.search).toHaveBeenCalledWith(indexName, query, { - LIMIT: { from: 0, size: 5 }, - RETURN: '$' - }) - }) - - it("returns no results", async () => expect(entities).toHaveLength(0)) - }) - - describe("when querying a single result", () => { - beforeEach(async () => { - mockClientSearchToReturnSingleJsonString() - entities = await jsonSearch.return.page(0, 5) - }) - - it("askes the client for results", () => { - expect(client.search).toHaveBeenCalledTimes(1) - expect(client.search).toHaveBeenCalledWith(indexName, query, { - LIMIT: { from: 0, size: 5 }, - RETURN: '$' - }) - }) - - it("returns the expected single result", () => { - expect(entities).toHaveLength(1) - expect(entities).toEqual(expect.arrayContaining([ - expect.objectContaining(SIMPLE_ENTITY_1) - ])) - }) - }) - - describe("when querying multiple results", () => { - beforeEach(async () => { - mockClientSearchToReturnMultipleJsonStrings() - entities = await jsonSearch.return.page(0, 5) - }) - - it("askes the client for results", () => { - expect(client.search).toHaveBeenCalledTimes(1) - expect(client.search).toHaveBeenCalledWith(indexName, query, { - LIMIT: { from: 0, size: 5 }, - RETURN: '$' - }) - }) - - it("returns all the expected results", () => { - expect(entities).toHaveLength(3) - expect(entities).toEqual(expect.arrayContaining([ - expect.objectContaining(SIMPLE_ENTITY_1), - expect.objectContaining(SIMPLE_ENTITY_2), - expect.objectContaining(SIMPLE_ENTITY_3) - ])) - }) - }) - }) - }) -}) diff --git a/spec/unit/search/search-sort-by.spec.ts b/spec/unit/search/search-sort-by.spec.ts deleted file mode 100644 index fe02a3a..0000000 --- a/spec/unit/search/search-sort-by.spec.ts +++ /dev/null @@ -1,365 +0,0 @@ -import '../helpers/mock-client' -import { Client } from "$lib/client" -import { Search, RawSearch } from "$lib/search" - -import { - simpleHashSchema, simpleSortableHashSchema, - simpleJsonSchema, simpleSortableJsonSchema -} from "../helpers/test-entity-and-schema" -import { - mockClientSearchToReturnMultipleHashes as hashMocker, - mockClientSearchToReturnMultipleJsonStrings as jsonMocker -} from '../helpers/search-helpers' -import { RedisOmError } from '$lib/error' - - -const warnSpy = vi.spyOn(global.console, 'warn').mockImplementation(() => {}) -const errorSpy = vi.spyOn(global.console, 'error').mockImplementation(() => {}) - -beforeEach(() => { - vi.mocked(client.search).mockReset() -}) - -const client = new Client() - -describe.each([ - ["FluentSearch", - new Search(simpleHashSchema, client), - new Search(simpleSortableHashSchema, client), - new Search(simpleJsonSchema, client), - new Search(simpleSortableJsonSchema, client)], - ["RawSearch", - new RawSearch(simpleHashSchema, client), - new RawSearch(simpleSortableHashSchema, client), - new RawSearch(simpleJsonSchema, client), - new RawSearch(simpleSortableJsonSchema, client)] -])("%s", (_, hashSearch, sortableHashSearch, jsonSearch, sortableJsonSearch) => { - - describe("on a Hash", () => { - - beforeEach(async () => { hashMocker() }) - - describe("#sortAscending", () => { - beforeEach(async () => { - await sortableHashSearch.sortAscending('aNumber').return.first() - }) - - it("asks the client for the results with the expected sort options", () => { - expect(client.search).toHaveBeenCalledWith('SimpleHashEntity:index', '*', { - LIMIT: { from: 0, size: 1 }, - SORTBY: { BY: 'aNumber', DIRECTION: 'ASC' } }) - }) - }) - - describe("#sortAsc", () => { - beforeEach(async () => { - await sortableHashSearch.sortAsc('aNumber').return.first() - }) - - it("asks the client for the results with the expected sort options", () => { - expect(client.search).toHaveBeenCalledWith('SimpleHashEntity:index', '*', { - LIMIT: { from: 0, size: 1 }, - SORTBY: { BY: 'aNumber', DIRECTION: 'ASC' } }) - }) - }) - - describe("#sortDescending", () => { - beforeEach(async () => { - await sortableHashSearch.sortDescending('aNumber').return.first() - }) - - it("asks the client for the results with the expected sort options", () => { - expect(client.search).toHaveBeenCalledWith('SimpleHashEntity:index', '*', { - LIMIT: { from: 0, size: 1 }, - SORTBY: { BY: 'aNumber', DIRECTION: 'DESC' } }) - }) - }) - - describe("#sortDesc", () => { - beforeEach(async () => { - await sortableHashSearch.sortDesc('aNumber').return.first() - }) - - it("asks the client for the results with the expected sort options", () => { - expect(client.search).toHaveBeenCalledWith('SimpleHashEntity:index', '*', { - LIMIT: { from: 0, size: 1 }, - SORTBY: { BY: 'aNumber', DIRECTION: 'DESC' } }) - }) - }) - }) - - describe("on a JSON Document", () => { - - beforeEach(async () => { jsonMocker() }) - - describe("#sortAscending", () => { - beforeEach(async () => { - await sortableJsonSearch.sortAscending('aNumber').return.first() - }) - - it("asks the client for the results with the expected sort options", () => { - expect(client.search).toHaveBeenCalledWith('SimpleJsonEntity:index', '*', { - LIMIT: { from: 0, size: 1 }, - SORTBY: { BY: 'aNumber', DIRECTION: 'ASC' }, - RETURN: '$' - }) - }) - }) - - describe("#sortAsc", () => { - beforeEach(async () => { - await sortableJsonSearch.sortAsc('aNumber').return.first() - }) - - it("asks the client for the results with the expected sort options", () => { - expect(client.search).toHaveBeenCalledWith('SimpleJsonEntity:index', '*', { - LIMIT: { from: 0, size: 1 }, - SORTBY: { BY: 'aNumber', DIRECTION: 'ASC' }, - RETURN: '$' - }) - }) - }) - - describe("#sortDescending", () => { - beforeEach(async () => { - await sortableJsonSearch.sortDescending('aNumber').return.first() - }) - - it("asks the client for the results with the expected sort options", () => { - expect(client.search).toHaveBeenCalledWith('SimpleJsonEntity:index', '*', { - LIMIT: { from: 0, size: 1 }, - SORTBY: { BY: 'aNumber', DIRECTION: 'DESC' }, - RETURN: '$' - }) - }) - }) - - describe("#sortDesc", () => { - beforeEach(async () => { - await sortableJsonSearch.sortDesc('aNumber').return.first() - }) - - it("asks the client for the results with the expected sort options", () => { - expect(client.search).toHaveBeenCalledWith('SimpleJsonEntity:index', '*', { - LIMIT: { from: 0, size: 1 }, - SORTBY: { BY: 'aNumber', DIRECTION: 'DESC' }, - RETURN: '$' - }) - }) - }) - }) - - describe("#sortBy", () => { - describe.each([ - - ["on a number in a Hash", hashSearch, hashMocker, - { - field: 'aNumber', - sortOrder: 'ASC', - expectedWarning: "'sortBy' was called on field 'aNumber' which is not marked as sortable in the Schema. This may result is slower searches. If possible, mark the field as sortable in the Schema." - }], - ["on a sortable number in a Hash", sortableHashSearch, hashMocker, - { - field: 'aNumber', - sortOrder: 'ASC', - }], - ["on a number in a JSON Document", jsonSearch, jsonMocker, - { - field: 'aNumber', - sortOrder: 'ASC', - expectedWarning: "'sortBy' was called on field 'aNumber' which is not marked as sortable in the Schema. This may result is slower searches. If possible, mark the field as sortable in the Schema." - }], - ["on a sortable number in a JSON Document", sortableJsonSearch, jsonMocker, - { - field: 'aNumber', - sortOrder: 'ASC', - }], - - ["on a string in a Hash", hashSearch, hashMocker, - { - field: 'aString', - sortOrder: 'ASC', - expectedWarning: "'sortBy' was called on field 'aString' which is not marked as sortable in the Schema. This may result is slower searches. If possible, mark the field as sortable in the Schema." - }], - ["on a sortable string in a Hash", sortableHashSearch, hashMocker, - { - field: 'aString', - sortOrder: 'ASC', - }], - ["on a string in a JSON Document", jsonSearch, jsonMocker, - { - field: 'aString', - sortOrder: 'ASC', - }], - ["on a sortable string in a JSON Document", sortableJsonSearch, jsonMocker, - { - field: 'aString', - sortOrder: 'ASC', - }], - - ["on a boolean in a Hash", hashSearch, hashMocker, - { - field: 'aBoolean', - sortOrder: 'ASC', - expectedWarning: "'sortBy' was called on field 'aBoolean' which is not marked as sortable in the Schema. This may result is slower searches. If possible, mark the field as sortable in the Schema." - }], - ["on a sortable boolean in a Hash", sortableHashSearch, hashMocker, - { - field: 'aBoolean', - sortOrder: 'ASC', - }], - ["on a boolean in a JSON Document", jsonSearch, jsonMocker, - { - field: 'aBoolean', - sortOrder: 'ASC', - }], - ["on a sortable boolean in a JSON Document", sortableJsonSearch, jsonMocker, - { - field: 'aBoolean', - sortOrder: 'ASC', - }], - - ["on a text in a Hash", hashSearch, hashMocker, - { - field: 'someText', - sortOrder: 'ASC', - expectedWarning: "'sortBy' was called on field 'someText' which is not marked as sortable in the Schema. This may result is slower searches. If possible, mark the field as sortable in the Schema." - }], - ["on a sortable text in a Hash", sortableHashSearch, hashMocker, - { - field: 'someText', - sortOrder: 'ASC', - }], - ["on a text in a JSON Document", jsonSearch, jsonMocker, - { - field: 'someText', - sortOrder: 'ASC', - expectedWarning: "'sortBy' was called on field 'someText' which is not marked as sortable in the Schema. This may result is slower searches. If possible, mark the field as sortable in the Schema." - }], - ["on a sortable text in a JSON Document", sortableJsonSearch, jsonMocker, - { - field: 'someText', - sortOrder: 'ASC', - }], - - ["on a point in a Hash", hashSearch, hashMocker, - { - field: 'aPoint', - sortOrder: 'ASC', - expectedError: "'sortBy' was called on 'point' field 'aPoint' which cannot be sorted." - }], - ["on a point in a JSON Document", jsonSearch, jsonMocker, - { - field: 'aPoint', - sortOrder: 'ASC', - expectedError: "'sortBy' was called on 'point' field 'aPoint' which cannot be sorted." - }], - - ["on a date in a Hash", hashSearch, hashMocker, - { - field: 'aDate', - sortOrder: 'ASC', - expectedWarning: "'sortBy' was called on field 'aDate' which is not marked as sortable in the Schema. This may result is slower searches. If possible, mark the field as sortable in the Schema." - }], - ["on a sortable date in a Hash", sortableHashSearch, hashMocker, - { - field: 'aDate', - sortOrder: 'ASC', - }], - ["on a date in a JSON Document", jsonSearch, jsonMocker, - { - field: 'aDate', - sortOrder: 'ASC', - expectedWarning: "'sortBy' was called on field 'aDate' which is not marked as sortable in the Schema. This may result is slower searches. If possible, mark the field as sortable in the Schema." - }], - ["on a sortable date in a JSON Document", sortableJsonSearch, jsonMocker, - { - field: 'aDate', - sortOrder: 'ASC', - }], - - ["on a array in a Hash", hashSearch, hashMocker, - { - field: 'someStrings', - sortOrder: 'ASC', - expectedError: "'sortBy' was called on 'string[]' field 'someStrings' which cannot be sorted." - }], - ["on a array in a JSON Document", jsonSearch, jsonMocker, - { - field: 'someStrings', - sortOrder: 'ASC', - expectedError: "'sortBy' was called on 'string[]' field 'someStrings' which cannot be sorted." - }], - - ["on an invalid field in a Hash", hashSearch, hashMocker, - { - field: 'somethingMissing', - sortOrder: 'ASC', - expectedError: "'sortBy' was called on field 'somethingMissing' which is not defined in the Schema." - }], - ["on an invalid field in a JSON Document", jsonSearch, jsonMocker, - { - field: 'somethingMissing', - sortOrder: 'ASC', - expectedError: "'sortBy' was called on field 'somethingMissing' which is not defined in the Schema." - }] - - ])("%s", (_, search, clientMocker, data: any) => { - - let field = data.field - let order = data.sortOrder - let expectedWarning = data.expectedWarning - let expectedError = data.expectedError - let actualError: RedisOmError - - beforeEach(async () => { - clientMocker() - try { - await search.sortBy(field, order).return.first() - } catch (error) { - actualError = error as RedisOmError - } - }) - - if (expectedError) { - it("logs and throws an error", () => { - expect(actualError!.message).toBe(expectedError) - expect(errorSpy).toHaveBeenCalledWith(expectedError) - }) - return - } - - it("does not generate an error", () => { - if (actualError) console.log(actualError) - expect(actualError).toBeUndefined() - expect(errorSpy).not.toHaveBeenCalled() - }) - - it("asks the client for the results with the expected sort options", () => { - // ya, this is a little jank - if (clientMocker === jsonMocker) { - expect(client.search).toHaveBeenCalledWith('SimpleJsonEntity:index', '*', { - LIMIT: { from: 0, size: 1 }, - SORTBY: { BY: field, DIRECTION: order }, - RETURN: '$' - }) - } else { - expect(client.search).toHaveBeenCalledWith('SimpleHashEntity:index', '*', { - LIMIT: { from: 0, size: 1 }, - SORTBY: { BY: field, DIRECTION: order } - }) - } - }) - - if (expectedWarning) { - it("generates the expected warning", () => { - expect(warnSpy).toHaveBeenCalledWith(expectedWarning) - }) - } else { - it("does not generate a warning", () => { - expect(warnSpy).not.toHaveBeenCalled() - }) - } - }) - }) -}) diff --git a/spec/unit/transformer/from-redis-hash.spec.ts b/spec/unit/transformer/from-redis-hash.spec.ts deleted file mode 100644 index 9681545..0000000 --- a/spec/unit/transformer/from-redis-hash.spec.ts +++ /dev/null @@ -1,121 +0,0 @@ -import '../../helpers/custom-matchers' - -import { RedisHashData } from "$lib/client" -import { EntityData } from "$lib/entity" -import { Schema } from "$lib/schema" -import { fromRedisHash } from "$lib/transformer" - -import { A_DATE, A_DATE_EPOCH_STRING, A_NUMBER, A_NUMBER_STRING, A_POINT, A_POINT_STRING, A_STRING, SOME_STRINGS, SOME_STRINGS_JOINED, SOME_TEXT } from "../../helpers/example-data" -import { InvalidHashValue } from '$lib/error' - -describe("#fromRedisHash", () => { - - let schema: Schema - let actual: EntityData - - beforeEach(() => { - schema = new Schema('TestPrefix', { - aBoolean: { type: 'boolean' }, - anotherBoolean: { type: 'boolean', field: 'aRenamedBoolean' }, - aNumber: { type: 'number' }, - anotherNumber: { type: 'number', field: 'aRenamedNumber' }, - aDate: { type: 'date' }, - anotherDate: { type: 'date', field: 'aRenamedDate' }, - aPoint: { type: 'point' }, - anotherPoint: { type: 'point', field: 'aRenamedPoint' }, - aString: { type: 'string' }, - anotherString: { type: 'string', field: 'aRenamedString' }, - aStringArray: { type: 'string[]' }, - aSeparatedStringArray: { type: 'string[]', separator: ',' }, - anotherStringArray: { type: 'string[]', field: 'aRenamedStringArray' }, - someText: { type: 'text' }, - someOtherText: { type: 'text', field: 'someRenamedText' } - }) - }) - - describe("when converting an empty hash", () => { - beforeEach(() => { - actual = fromRedisHash(schema, {}) - }) - - it("returns an empty object", () => { - expect(actual).toEqual({}) - }) - }) - - describe("when converting data not described in the schema", () => { - beforeEach(() => { - actual = fromRedisHash(schema, { - aTrueBoolean: '1', aFalseBoolean: '0', - aMissingNumber: A_NUMBER_STRING, aMissingDate: A_DATE_EPOCH_STRING, - aMissingPoint: A_POINT_STRING, aMissingString: A_STRING - }) - }) - - it.each([ - ["leaves a converted true boolean as a string", { aTrueBoolean: '1' }], - ["leaves a converted false boolean as a string", { aFalseBoolean: '0' }], - ["leaves a converted number as a string", { aMissingNumber: A_NUMBER_STRING }], - ["leaves a converted date as an epoch string", { aMissingDate: A_DATE_EPOCH_STRING }], - ["leaves a converted point as a string", { aMissingPoint: A_POINT_STRING }], - ["leaves a string as a string", { aMissingString: A_STRING }] - ])('%s', (_, expected) => { - expect(actual).toEqual(expect.objectContaining(expected)) - }) - }) - - describe("when converting data that *is* described in the schema", () => { - - it.each([ - - // boolean - ["converts a true boolean from a string", { aBoolean: '1' }, { aBoolean: true }], - ["converts a false boolean from a string", { aBoolean: '0' }, { aBoolean: false }], - ["converts a renamed boolean from a string", { aRenamedBoolean: '1' }, { anotherBoolean: true }], - - // number - ["converts a number from a string", { aNumber: A_NUMBER_STRING }, { aNumber: A_NUMBER }], - ["converts a renamed number from a string", { aRenamedNumber: A_NUMBER_STRING }, { anotherNumber: A_NUMBER }], - - // date - ["converts a date from an epoch string", { aDate: A_DATE_EPOCH_STRING }, { aDate: A_DATE }], - ["converts a renamed date from an epoch string", { aRenamedDate: A_DATE_EPOCH_STRING }, { anotherDate: A_DATE }], - - // point - ["converts a point from a point string", { aPoint: A_POINT_STRING }, { aPoint: A_POINT }], - ["converts a renamed point from a point string", { aRenamedPoint: A_POINT_STRING }, { anotherPoint: A_POINT }], - - // string - ["leaves a string as a string", { aString: A_STRING }, { aString: A_STRING }], - ["leaves a renamed string as a string", { aRenamedString: A_STRING }, { anotherString: A_STRING }], - - // text - ["converts a string in a text from a string", { someText: SOME_TEXT }, { someText: SOME_TEXT }], - ["converts a renamed string in a text from a string", { someRenamedText: SOME_TEXT }, { someOtherText: SOME_TEXT }], - - // string[] - ["converts a string[] from a delimited string", { aStringArray: SOME_STRINGS_JOINED }, { aStringArray: SOME_STRINGS }], - ["converts a string[] from a string without delimiters", { aStringArray: A_STRING }, { aStringArray: [ A_STRING ] }], - ["converts a renamed string[] from a delimited string", { aRenamedStringArray: SOME_STRINGS_JOINED }, { anotherStringArray: SOME_STRINGS }], - ["converts a string[] from a delimited string with a custom delimiter", { aSeparatedStringArray: SOME_STRINGS.join(',') }, { aSeparatedStringArray: SOME_STRINGS }] - - ])('%s', (_, hashData: RedisHashData, expected) => { - const actual = fromRedisHash(schema, hashData) - expect(actual).not.toBe(hashData) - expect(actual).toEqual(expected) - }) - - it.each([ - ["complains when a boolean is invalid", { aBoolean: 'NOT_A_BOOLEAN' }, `Unexpected value for field 'aBoolean' of type 'boolean' from Hash field "aBoolean" read from Redis.`], - ["complains when an aliased boolean is invalid", { aRenamedBoolean: 'NOT_A_BOOLEAN' }, `Unexpected value for field 'anotherBoolean' of type 'boolean' from Hash field "aRenamedBoolean" read from Redis.`], - ["complains when a number is not numeric", { aNumber: 'NOT_A_NUMBER' }, `Unexpected value for field 'aNumber' of type 'number' from Hash field "aNumber" read from Redis.`], - ["complains when an alaised number is not numeric", { aRenamedNumber: 'NOT_A_NUMBER' }, `Unexpected value for field 'anotherNumber' of type 'number' from Hash field "aRenamedNumber" read from Redis.`], - ["complains when a date is not an epoch string", { aDate: 'NOT_A_NUMBER' }, `Unexpected value for field 'aDate' of type 'date' from Hash field "aDate" read from Redis.`], - ["complains when an aliased date is not an epoch string", { aRenamedDate: 'NOT_A_NUMBER' }, `Unexpected value for field 'anotherDate' of type 'date' from Hash field "aRenamedDate" read from Redis.`], - ["complains when a point is not a point string", { aPoint: 'NOT_A_POINT' }, `Unexpected value for field 'aPoint' of type 'point' from Hash field "aPoint" read from Redis.`], - ["complains when an aliased point is not a point string", { aRenamedPoint: 'NOT_A_POINT' }, `Unexpected value for field 'anotherPoint' of type 'point' from Hash field "aRenamedPoint" read from Redis.`], - ])('%s', (_, hashData: RedisHashData, expectedMessage) => { - expect(() => fromRedisHash(schema, hashData)).toThrowErrorOfType(InvalidHashValue, expectedMessage) - }) - }) -}) diff --git a/spec/unit/transformer/from-redis-json.spec.ts b/spec/unit/transformer/from-redis-json.spec.ts deleted file mode 100644 index 6bffece..0000000 --- a/spec/unit/transformer/from-redis-json.spec.ts +++ /dev/null @@ -1,198 +0,0 @@ -import '../../helpers/custom-matchers' - -import { RedisJsonData } from "$lib/client" -import { EntityData } from "$lib/entity" -import { Schema } from "$lib/schema" -import { fromRedisJson } from "$lib/transformer" -import { InvalidJsonValue, NullJsonValue } from '$lib/error' - -import { ANOTHER_STRING, A_DATE, A_DATE_EPOCH, A_DATE_EPOCH_STRING, A_NUMBER, A_NUMBER_STRING, A_POINT, A_POINT_STRING, A_STRING, A_THIRD_STRING, SOME_STRINGS, SOME_TEXT } from "../../helpers/example-data" - -describe("#fromRedisJson", () => { - - let schema: Schema - let actual: EntityData - - beforeEach(() => { - schema = new Schema('TestPrefix', { - aBoolean: { type: 'boolean' }, - anotherBoolean: { type: 'boolean', path: '$.aPathedBoolean' }, - aNestedBoolean: { type: 'boolean', path: '$.aNestedBoolean.aBoolean' }, - someBooleans: { type: 'boolean', path: '$.someBooleans[*]' }, - aNumber: { type: 'number' }, - anotherNumber: { type: 'number', path: '$.aPathedNumber' }, - aNestedNumber: { type: 'number', path: '$.aNestedNumber.aNumber' }, - someNumbers: { type: 'number', path: '$.someNumbers[*]' }, - aDate: { type: 'date' }, - anotherDate: { type: 'date', path: '$.aPathedDate' }, - aNestedDate: { type: 'date', path: '$.aNestedDate.aDate' }, - someDates: { type: 'number', path: '$.someDates[*]' }, - aPoint: { type: 'point' }, - anotherPoint: { type: 'point', path: '$.aPathedPoint' }, - aNestedPoint: { type: 'point', path: '$.aNestedPoint.aPoint' }, - somePoints: { type: 'point', path: '$.somePoints[*]' }, - aString: { type: 'string' }, - anotherString: { type: 'string', path: '$.aPathedString' }, - aNestedString: { type: 'string', path: '$.aNestedString.aString' }, - someStrings: { type: 'string', path: '$.someStrings[*]' }, - someText: { type: 'text' }, - someMoreText: { type: 'text', path: '$.somePathedText' }, - someNestedText: { type: 'text', path: '$.someNestedText.someText' }, - arrayOfText: { type: 'text', path: '$.arrayOfText[*]' }, - aStringArray: { type: 'string[]' }, - someStringsAsAnArray: { type: 'string[]', path: '$.someOtherStrings[*]' }, - someOtherStringsAsAnArray: { type: 'string[]', path: '$.someObjects[*].aString' } - }) - }) - - describe("when converting an empty hash", () => { - beforeEach(() => { - actual = fromRedisJson(schema, {}) - }) - - it("returns an empty object", () => { - expect(actual).toEqual({}) - }) - }) - - describe("when converting data not described in the schema", () => { - beforeEach(() => { - actual = fromRedisJson(schema, { - aTrueBoolean: true, aFalseBoolean: false, - aMissingNumber: A_NUMBER, aMissingDate: A_DATE_EPOCH_STRING, - aMissingPoint: A_POINT_STRING, aMissingString: A_STRING, - aMissingEmptyArray: [], - aMissingSingleArray: [ A_STRING ], - aMissingArray: [ A_STRING, A_NUMBER, true ], - aMissingObject: { withArray: [ A_STRING, A_NUMBER, true ] } - }) - }) - - it.each([ - ["leaves a true boolean as true", { aTrueBoolean: true }], - ["leaves a false boolean as false", { aFalseBoolean: false }], - ["leaves a number as a number", { aMissingNumber: A_NUMBER }], - ["leaves a converted date as a string", { aMissingDate: A_DATE_EPOCH_STRING }], - ["leaves a converted point as a string", { aMissingPoint: A_POINT_STRING }], - ["leaves a string as a string", { aMissingString: A_STRING }], - ["leaves an empty array empty", { aMissingEmptyArray: [] }], - ["leaves a lonely array alone", { aMissingSingleArray: [ A_STRING ] }], - ["leaves a populated array populated", { aMissingArray: [ A_STRING, A_NUMBER, true ] }], - ["leaves nested objects as objects", { aMissingObject: { withArray: [ A_STRING, A_NUMBER, true ] } }] - ])('%s', (_, expected) => { - expect(actual).toEqual(expect.objectContaining(expected)) - }) - }) - - describe("when converting data that *is* described in the schema", () => { - - it.each([ - - // boolean - ["leaves a true boolean as a boolean", { aBoolean: true }, { aBoolean: true }], - ["leaves a false boolean as a string", { aBoolean: false }, { aBoolean: false }], - ["leaves a null boolean as null", { aBoolean: null }, { aBoolean: null }], - ["leaves a pathed boolean as a boolean", { aPathedBoolean: true }, { aPathedBoolean: true }], - ["leaves a nested boolean as a boolean", { aNestedBoolean: { aBoolean: true } }, { aNestedBoolean: { aBoolean: true } }], - - // number - ["leaves a number as a number", { aNumber: A_NUMBER }, { aNumber: A_NUMBER }], - ["leaves a null number as null", { aNumber: null }, { aNumber: null }], - ["leaves a pathed number as a number", { aPathedNumber: A_NUMBER }, { aPathedNumber: A_NUMBER }], - ["leaves a nested number as a number", { aNestedNumber: { aNumber: A_NUMBER } }, { aNestedNumber: { aNumber: A_NUMBER } }], - - // date - ["coerces a epoch date to a date", { aDate: A_DATE_EPOCH }, { aDate: A_DATE }], - ["leaves a null date as null", { aDate: null }, { aDate: null }], - ["coerces a pathed epoch date to a date", { aPathedDate: A_DATE_EPOCH }, { aPathedDate: A_DATE }], - ["coerces a nested epoch date to a date", { aNestedDate: { aDate: A_DATE_EPOCH } }, { aNestedDate: { aDate: A_DATE } }], - - // point - ["converts a point string to a point", { aPoint: A_POINT_STRING }, { aPoint: A_POINT }], - ["leaves a null point as null", { aPoint: null }, { aPoint: null }], - ["converts a pathed point string to a point", { aPathedPoint: A_POINT_STRING }, { aPathedPoint: A_POINT }], - ["converts a pathed point string to a point", { aNestedPoint: { aPoint: A_POINT_STRING } }, { aNestedPoint: { aPoint: A_POINT } }], - - // string - ["leaves a string as a string", { aString: A_STRING }, { aString: A_STRING }], - ["leaves a null string as null", { aString: null }, { aString: null }], - ["coerces a number to a string", { aString: A_NUMBER }, { aString: A_NUMBER_STRING }], - ["coerces a boolean to a string", { aString: true }, { aString: 'true' }], - ["leaves a pathed string as a string", { aPathedString: A_STRING }, { aPathedString: A_STRING }], - ["leaves a nested string as a string", { aNestedString: { aString: A_STRING } }, { aNestedString: { aString: A_STRING } }], - - // text - ["leaves a text as a string", { someText: SOME_TEXT }, { someText: SOME_TEXT }], - ["leaves a null text as null", { someText: null }, { someText: null }], - ["coerces a number text to a string", { someText: A_NUMBER }, { someText: A_NUMBER_STRING }], - ["coerces a boolean text to a string", { someText: true }, { someText: 'true' }], - ["leaves a pathed string as a string", { somePathedText: SOME_TEXT }, { somePathedText: SOME_TEXT }], - ["leaves a nested string as a string", { someNestedText: { someText: SOME_TEXT } }, { someNestedText: { someText: SOME_TEXT } }], - - // string[] - ["leaves an empty string[] empty", { aStringArray: [] }, { aStringArray: [] }], - ["leaves a lonely string[] alone", { aStringArray: [ A_STRING ] }, { aStringArray: [ A_STRING] }], - ["leaves a populated string[] populated", { aStringArray: SOME_STRINGS }, { aStringArray: SOME_STRINGS }], - ["leaves a null string[] as null", { aStringArray: null }, { aStringArray: null }], - ["leaves a string[] that doesn't contain a string[] as is", { aStringArray: 'NOT_AN_ARRAY' }, { aStringArray: 'NOT_AN_ARRAY' }], - ["coerces numbers and booleans in a string[] to strings", { aStringArray: [ A_STRING, A_NUMBER, true] }, { aStringArray: [A_STRING, A_NUMBER_STRING, 'true'] }], - - // dispersed string[] - ["leaves dispersed string[] as strings", { someOtherStrings: SOME_STRINGS }, { someOtherStrings: SOME_STRINGS }], - ["coerces numbers and booleans in a dispersed string[] to strings", - { someOtherStrings: [ A_STRING, A_NUMBER, true ] }, - { someOtherStrings: [ A_STRING, A_NUMBER_STRING, 'true' ] }], - - // widely dispersed string[] - ["leaves widely dispersed string[] as strings", - { someObjects: [ { aString: A_STRING }, { aString: ANOTHER_STRING }, { aString: A_THIRD_STRING } ] }, - { someObjects: [ { aString: A_STRING }, { aString: ANOTHER_STRING }, { aString: A_THIRD_STRING } ] }], - ["coerces numbers and booleans in a widely dispersed string[] to strings", - { someObjects: [ { aString: A_STRING }, { aString: A_NUMBER }, { aString: true } ] }, - { someObjects: [ { aString: A_STRING }, { aString: A_NUMBER_STRING }, { aString: 'true' } ] }], - - ])('%s', (_, jsonData: RedisJsonData, expected) => { - const actual = fromRedisJson(schema, jsonData) - expect(actual).toEqual(expected) - }) - - it.each([ - ["complains when a boolean is invalid", { aBoolean: 'NOT_A_BOOLEAN' }, `Unexpected value for field 'aBoolean' of type 'boolean' from JSON path "$.aBoolean" in Redis.`], - ["complains when a pathed boolean is invalid", { aPathedBoolean: 'NOT_A_BOOLEAN' }, `Unexpected value for field 'anotherBoolean' of type 'boolean' from JSON path "$.aPathedBoolean" in Redis.`], - ["complains when a nested boolean is invalid", { aNestedBoolean: { aBoolean: 'NOT_A_BOOLEAN' } }, `Unexpected value for field 'aNestedBoolean' of type 'boolean' from JSON path "$.aNestedBoolean.aBoolean" in Redis.`], - - ["complains when a number is invalid", { aNumber: 'NOT_A_NUMBER' }, `Unexpected value for field 'aNumber' of type 'number' from JSON path "$.aNumber" in Redis.`], - ["complains when a pathed number is invalid", { aPathedNumber: 'NOT_A_NUMBER' }, `Unexpected value for field 'anotherNumber' of type 'number' from JSON path "$.aPathedNumber" in Redis.`], - ["complains when a nested number is invalid", { aNestedNumber: { aNumber: 'NOT_A_NUMBER' } }, `Unexpected value for field 'aNestedNumber' of type 'number' from JSON path "$.aNestedNumber.aNumber" in Redis.`], - - ["complains when a date is invalid", { aDate: 'NOT_A_NUMBER' }, `Unexpected value for field 'aDate' of type 'date' from JSON path "$.aDate" in Redis.`], - ["complains when a pathed date is invalid", { aPathedDate: 'NOT_A_NUMBER' }, `Unexpected value for field 'anotherDate' of type 'date' from JSON path "$.aPathedDate" in Redis.`], - ["complains when a nested date is invalid", { aNestedDate: { aDate: 'NOT_A_NUMBER' } }, `Unexpected value for field 'aNestedDate' of type 'date' from JSON path "$.aNestedDate.aDate" in Redis.`], - - ["complains when a point is not a point string", { aPoint: 'NOT_A_POINT' }, `Unexpected value for field 'aPoint' of type 'point' from JSON path "$.aPoint" in Redis.`], - ["complains when a point is not a string", { aPoint: A_NUMBER }, `Unexpected value for field 'aPoint' of type 'point' from JSON path "$.aPoint" in Redis.`], - ["complains when a pathed point is invalid", { aPathedPoint: 'NOT_A_POINT' }, `Unexpected value for field 'anotherPoint' of type 'point' from JSON path "$.aPathedPoint" in Redis.`], - ["complains when a nested point is invalid", { aNestedPoint: { aPoint: 'NOT_A_POINT' } }, `Unexpected value for field 'aNestedPoint' of type 'point' from JSON path "$.aNestedPoint.aPoint" in Redis.`], - - ["complains when a string is invalid", { aString: SOME_STRINGS }, `Unexpected value for field 'aString' of type 'string' from JSON path "$.aString" in Redis.`], - ["complains when a pathed string is invalid", { aPathedString: SOME_STRINGS }, `Unexpected value for field 'anotherString' of type 'string' from JSON path "$.aPathedString" in Redis.`], - ["complains when a nested string is invalid", { aNestedString: { aString: SOME_STRINGS } }, `Unexpected value for field 'aNestedString' of type 'string' from JSON path "$.aNestedString.aString" in Redis.`], - - ["complains when text is invalid", { someText: SOME_STRINGS }, `Unexpected value for field 'someText' of type 'text' from JSON path "$.someText" in Redis.`], - ["complains when pathed text is invalid", { somePathedText: SOME_STRINGS }, `Unexpected value for field 'someMoreText' of type 'text' from JSON path "$.somePathedText" in Redis.`], - ["complains when nested text is invalid", { someNestedText: { someText: SOME_STRINGS } }, `Unexpected value for field 'someNestedText' of type 'text' from JSON path "$.someNestedText.someText" in Redis.`], - - ])('%s', (_, jsonData: RedisJsonData, expectedMessage) => { - expect(() => fromRedisJson(schema, jsonData)).toThrowErrorOfType(InvalidJsonValue, expectedMessage) - }) - - it.each([ - ["complains when a string[] contains a null", { aStringArray: [ A_STRING, null, ANOTHER_STRING ] }, `Null or undefined found in field 'aStringArray' of type 'string[]' from JSON path "$.aStringArray[*]" in Redis.`], - ["complains when a dispersed string[] contains null", { someOtherStrings: [ A_STRING, null, ANOTHER_STRING] }, `Null or undefined found in field 'someStringsAsAnArray' of type 'string[]' from JSON path "$.someOtherStrings[*]" in Redis.`], - ["complains when a widely dispersed string[] contains null", { someObjects: [ { aString: A_STRING }, { aString: null }, { aString: ANOTHER_STRING } ] }, `Null or undefined found in field 'someOtherStringsAsAnArray' of type 'string[]' from JSON path "$.someObjects[*].aString" in Redis.`] - - ])('%s', (_, jsonData: RedisJsonData, expectedMessage) => { - expect(() => fromRedisJson(schema, jsonData)).toThrowErrorOfType(NullJsonValue, expectedMessage) - }) - }) -}) diff --git a/spec/unit/transformer/to-redis-hash.spec.ts b/spec/unit/transformer/to-redis-hash.spec.ts deleted file mode 100644 index 4bdcf8a..0000000 --- a/spec/unit/transformer/to-redis-hash.spec.ts +++ /dev/null @@ -1,180 +0,0 @@ -import '../../helpers/custom-matchers' - -import { RedisHashData } from "$lib/client" -import { Schema } from "$lib/schema" -import { toRedisHash } from "$lib/transformer" - -import { AN_INVALID_POINT, A_DATE, A_DATE_EPOCH, A_DATE_EPOCH_STRING, A_DATE_ISO, A_NUMBER, A_NUMBER_STRING, A_PARITAL_POINT, A_POINT, A_POINT_PRETTY_JSON, A_POINT_STRING, A_STRING, SOME_STRINGS, SOME_STRINGS_JOINED, SOME_TEXT } from "../../helpers/example-data" -import { InvalidHashInput, NestedHashInput, PointOutOfRange, ArrayHashInput } from '$lib/error' - - -describe("#toRedisHash", () => { - - let schema: Schema - let actual: RedisHashData - - describe("when converting data not described in the schema", () => { - beforeEach(() => { - schema = new Schema('TestPrefix', {}) - actual = toRedisHash(schema, { - aTrueBoolean: true, aFalseBoolean: false, - aNumber: A_NUMBER, aDate: A_DATE, aPoint: A_POINT, - aString: A_STRING, somethingUndefined: undefined, aNull: null - }) - }) - - it.each([ - ["converts a true boolean to a string", { aTrueBoolean: '1' }], - ["converts a false boolean to a string", { aFalseBoolean: '0' }], - ["converts a number to a string", { aNumber: A_NUMBER_STRING }], - ["converts a date to an epoch string", { aDate: A_DATE_EPOCH_STRING }], - ["converts a point to a string", { aPoint: A_POINT_STRING }], - ["leaves a string as a string", { aString: A_STRING }] - ])('%s', (_, expected) => { - expect(actual).toEqual(expect.objectContaining(expected)) - }) - - it("removes an explicit undefined", () => expect(actual).not.toHaveProperty('somethingUndefined')) - it("removes a null", () => expect(actual).not.toHaveProperty('aNull')) - - it("complains when given an array", () => { - expect(() => toRedisHash(schema, { anArray: [A_STRING, A_NUMBER, true]})) - .toThrowErrorOfType(ArrayHashInput, "Unexpected array in Hash at property 'anArray'. You can not store an array in a Redis Hash without defining it in the Schema.") - }) - - it("complains when given an object", () => { - expect(() => toRedisHash(schema, { anObject: { aString: A_STRING, aNumber: A_NUMBER, aBoolean: true }})) - .toThrowErrorOfType(NestedHashInput, "Unexpected object in Hash at property 'anObject'. You can not store a nested object in a Redis Hash.") - }) - - it("complains when given an invalid point", () => { - expect(() => toRedisHash(schema, { aBadPoint: AN_INVALID_POINT })) - .toThrowErrorOfType(PointOutOfRange, "Points must be between ±85.05112878 latitude and ±180 longitude.") - }) - }) - - describe("when converting data that *is* described in the schema", () => { - beforeEach(() => { - schema = new Schema('TestPrefix', { - aBoolean: { type: 'boolean' }, - anotherBoolean: { type: 'boolean', field: 'aRenamedBoolean' }, - aNumber: { type: 'number' }, - anotherNumber: { type: 'number', field: 'aRenamedNumber' }, - aDate: { type: 'date' }, - anotherDate: { type: 'date', field: 'aRenamedDate' }, - aPoint: { type: 'point' }, - anotherPoint: { type: 'point', field: 'aRenamedPoint' }, - aString: { type: 'string' }, - anotherString: { type: 'string', field: 'aRenamedString' }, - aStringArray: { type: 'string[]' }, - aSeparatedStringArray: { type: 'string[]', separator: ',' }, - anotherStringArray: { type: 'string[]', field: 'aRenamedStringArray' }, - someText: { type: 'text' }, - someOtherText: { type: 'text', field: 'someRenamedText' } - }) - }) - - it("doesn't add missing fields", () => { - const actual = toRedisHash(schema, {}) - expect(actual).toEqual({}) - }) - - it.each([ - - // boolean - ["converts a true boolean to a string", { aBoolean: true }, { aBoolean: '1' }], - ["converts a false boolean to a string", { aBoolean: false }, { aBoolean: '0' }], - ["converts a renamed boolean to a string", { anotherBoolean: true }, { aRenamedBoolean: '1' }], - ["removes an explicitly undefined boolean", { aBoolean: undefined }, {}], - ["removes a null boolean", { aBoolean: null }, {}], - - // number - ["converts a number to a string", { aNumber: A_NUMBER }, { aNumber: A_NUMBER_STRING }], - ["converts a renamed number to a string", { anotherNumber: A_NUMBER }, { aRenamedNumber: A_NUMBER_STRING }], - ["removes an explicitly undefined number", { aNumber: undefined }, {}], - ["removes a null number", { aNumber: null }, {}], - - // date - ["converts a date to an epoch string", { aDate: A_DATE }, { aDate: A_DATE_EPOCH_STRING }], - ["converts an ISO date to an epoch string", { aDate: A_DATE_ISO }, { aDate: A_DATE_EPOCH_STRING }], - ["converts an UNIX epoch date to an epoch string", { aDate: A_DATE_EPOCH }, { aDate: A_DATE_EPOCH_STRING }], - ["converts a renamed date to an epoch string", { anotherDate: A_DATE }, { aRenamedDate: A_DATE_EPOCH_STRING }], - ["removes an explicitly undefined date", { aDate: undefined }, {}], - ["removes a null date", { aDate: null }, {}], - - // point - ["converts a point to a string", { aPoint: A_POINT }, { aPoint: A_POINT_STRING }], - ["converts a renamed point to a string", { anotherPoint: A_POINT }, { aRenamedPoint: A_POINT_STRING }], - ["removes an explicitly undefined point", { aPoint: undefined }, {}], - ["removes a null point", { aPoint: null }, {}], - - // string - ["leaves a string as a string", { aString: A_STRING }, { aString: A_STRING }], - ["coerces a number to a string", { aString: A_NUMBER }, { aString: A_NUMBER_STRING }], - ["coerces a boolean to a string", { aString: true }, { aString: 'true' }], - ["leaves a renamed string as a string", { anotherString: A_STRING }, { aRenamedString: A_STRING }], - ["removes an explicitly undefined string", { aString: undefined }, {}], - ["removes a null string", { aString: null }, {}], - - // text - ["converts a string in a text to a string", { someText: SOME_TEXT }, { someText: SOME_TEXT }], - ["coerces a number in a text to a string", { someText: A_NUMBER }, { someText: A_NUMBER_STRING }], - ["coerces a boolean in a text to a string", { someText: true }, { someText: 'true' }], - ["converts a renamed string in a text to a string", { someOtherText: SOME_TEXT }, { someRenamedText: SOME_TEXT }], - ["removes an explicitly undefined text", { someText: undefined }, {}], - ["removes a null text", { someText: null }, {}], - - // string[] - ["converts a string[] to a delimited string", { aStringArray: SOME_STRINGS }, { aStringArray: SOME_STRINGS_JOINED }], - ["converts a renamed string[] to a delimited string", { anotherStringArray: SOME_STRINGS }, { aRenamedStringArray: SOME_STRINGS_JOINED }], - ["coerces numbers and booleans in a string[] to strings", { aStringArray: [ A_STRING, A_NUMBER, true] }, { aStringArray: [A_STRING, A_NUMBER_STRING, 'true'].join('|') }], - ["converts a string[] to a delimited string with a custom delimiter", { aSeparatedStringArray: SOME_STRINGS }, { aSeparatedStringArray: SOME_STRINGS.join(',') }], - ["coerces numbers and booleans in a string[] to string with a custom delimiter", { aSeparatedStringArray: [ A_STRING, A_NUMBER, true] }, { aSeparatedStringArray: [A_STRING, A_NUMBER_STRING, 'true'].join(',') }], - - ])('%s', (_, data, expected) => { - const actual = toRedisHash(schema, data) - expect(actual).not.toBe(data) - expect(actual).toEqual(expected) - }) - - it("complains when given an invalid point", () => { - expect(() => toRedisHash(schema, { aPoint: AN_INVALID_POINT })) - .toThrowErrorOfType(PointOutOfRange, "Points must be between ±85.05112878 latitude and ±180 longitude.") - }) - - it.each([ - - // boolean - ["complains when a boolean is not a boolean", { aBoolean: 'NOT_A_BOOLEAN' }, `Unexpected value for field 'aBoolean' of type 'boolean' in Hash.`], - ["complains when a boolean is an object", { aBoolean: A_POINT }, `Unexpected value for field 'aBoolean' of type 'boolean' in Hash.`], - - // number - ["complains when a number is not a number", { aNumber: 'NOT_A_NUMBER' }, `Unexpected value for field 'aNumber' of type 'number' in Hash.`], - ["complains when a number is an object", { aNumber: A_POINT }, `Unexpected value for field 'aNumber' of type 'number' in Hash.`], - - /// date - ["complains when a date is not a date", { aDate: true }, `Unexpected value for field 'aDate' of type 'date' in Hash.`], - ["complains when a date is not a date string", { aDate: 'NOT_A_DATE' }, `Unexpected value for field 'aDate' of type 'date' in Hash.`], - ["complains when a date is an object", { aDate: A_POINT }, `Unexpected value for field 'aDate' of type 'date' in Hash.`], - - // point - ["complains when a point is not a point", { aPoint: 'NOT_A_POINT' }, `Unexpected value for field 'aPoint' of type 'point' in Hash.`], - ["complains when a point is a partial point", { aPoint: A_PARITAL_POINT }, `Unexpected value for field 'aPoint' of type 'point' in Hash.`], - - // string - ["complains when a string is not a string", { aString: A_DATE }, `Unexpected value for field 'aString' of type 'string' in Hash.`], - ["complains when a string is an object", { aString: A_POINT }, `Unexpected value for field 'aString' of type 'string' in Hash.`], - - // text - ["complains when a text is not a string", { someText: A_DATE }, `Unexpected value for field 'someText' of type 'text' in Hash.`], - ["complains when a text is an object", { someText: A_POINT }, `Unexpected value for field 'someText' of type 'text' in Hash.`], - - // string[] - ["complains when a string[] is not an array", { aStringArray: 'NOT_AN_ARRAY' }, `Unexpected value for field 'aStringArray' of type 'string[]' in Hash.`], - ["complains when a string[] is an object", { aStringArray: A_POINT }, `Unexpected value for field 'aStringArray' of type 'string[]' in Hash.`], - - ])('%s', (_, data, expectedMessage) => { - expect(() => toRedisHash(schema, data)).toThrowErrorOfType(InvalidHashInput, expectedMessage) - }) - }) -}) diff --git a/spec/unit/transformer/to-redis-json.spec.ts b/spec/unit/transformer/to-redis-json.spec.ts deleted file mode 100644 index c678cf2..0000000 --- a/spec/unit/transformer/to-redis-json.spec.ts +++ /dev/null @@ -1,261 +0,0 @@ -import '../../helpers/custom-matchers' - -import { RedisJsonData } from "$lib/client" -import { InvalidJsonInput, NullJsonInput, PointOutOfRange } from "$lib/error" -import { Schema } from "$lib/schema" -import { toRedisJson } from "$lib/transformer" - -import { ANOTHER_STRING, AN_INVALID_POINT, A_DATE, A_DATE_EPOCH, A_DATE_ISO, A_NUMBER, A_NUMBER_STRING, A_PARITAL_POINT, A_POINT, A_POINT_STRING, A_STRING, A_THIRD_STRING, SOME_STRINGS, SOME_TEXT } from "../../helpers/example-data" - - -describe("#toRedisJson", () => { - - let schema: Schema - let actual: RedisJsonData - - describe("when converting data not described in the schema", () => { - beforeEach(() => { - schema = new Schema('TestPrefix', {}) - actual = toRedisJson(schema, { - aTrueBoolean: true, aFalseBoolean: false, aNumber: A_NUMBER, aString: A_STRING, - aDate: A_DATE, aNestedDate: { aDate: A_DATE }, - aPoint: A_POINT, aNull: null, somethingUndefined: undefined, - aNestedUndefined: { somethingUndefined: undefined }, - anArray: [ A_STRING, A_NUMBER, true ], anEmptyArray: [], - anObject: { aString: A_STRING, aNumber: A_NUMBER, aBoolean: true }, anEmptyObject: {} - }) - }) - - it.each([ - ["leaves a true boolean as a boolean", { aTrueBoolean: true }], - ["leaves a false boolean as a boolean", { aFalseBoolean: false }], - ["leaves a a number as a number", { aNumber: A_NUMBER }], - ["leaves a string as a string", { aString: A_STRING }], - ["converts a date to an epoch number", { aDate: A_DATE_EPOCH }], - ["converts a nested date to an epoch number", { aNestedDate: { aDate: A_DATE_EPOCH } }], - ["leaves a point as an object", { aPoint: A_POINT }], - ["leaves a null as a null", { aNull: null }], - ["leaves an array as an array", { anArray: [ A_STRING, A_NUMBER, true ] }], - ["leaves an object as an object", { anObject: { aString: A_STRING, aNumber: A_NUMBER, aBoolean: true } }], - ["leaves an empty array as an array", { anEmptyArray: [] }], - ["leaves an empty object as an object", { anEmptyObject: {} }] - ])('%s', (_, expected) => { - expect(actual).toEqual(expect.objectContaining(expected)) - }) - - it("removes an explicit undefined", () => expect(actual).not.toHaveProperty('somethingUndefined')) - it("removes a nested explicit undefined", () => expect(actual.aNestedUndefined).not.toHaveProperty('somethingUndefined')) - it("leaves the containing object with a nested explicit undefined in place even though it is empty", - () => expect(actual.aNestedUndefined).toEqual({})) - }) - - describe("when converting data that *is* described in the schema", () => { - beforeEach(() => { - schema = new Schema('TestPrefix', { - aBoolean: { type: 'boolean' }, - anotherBoolean: { type: 'boolean', path: '$.aPathedBoolean' }, - aNestedBoolean: { type: 'boolean', path: '$.aNestedBoolean.aBoolean' }, - someBooleans: { type: 'boolean', path: '$.someBooleans[*]' }, - aNumber: { type: 'number' }, - anotherNumber: { type: 'number', path: '$.aPathedNumber' }, - aNestedNumber: { type: 'number', path: '$.aNestedNumber.aNumber' }, - someNumbers: { type: 'number', path: '$.someNumbers[*]' }, - aDate: { type: 'date' }, - anotherDate: { type: 'date', path: '$.aPathedDate' }, - aNestedDate: { type: 'date', path: '$.aNestedDate.aDate' }, - someDates: { type: 'date', path: '$.someDates[*]' }, - aPoint: { type: 'point' }, - anotherPoint: { type: 'point', path: '$.aPathedPoint' }, - aNestedPoint: { type: 'point', path: '$.aNestedPoint.aPoint' }, - somePoints: { type: 'point', path: '$.somePoints[*]' }, - aString: { type: 'string' }, - anotherString: { type: 'string', path: '$.aPathedString' }, - aNestedString: { type: 'string', path: '$.aNestedString.aString' }, - someStrings: { type: 'string', path: '$.someStrings[*]' }, - someText: { type: 'text' }, - someMoreText: { type: 'text', path: '$.somePathedText' }, - someNestedText: { type: 'text', path: '$.someNestedText.someText' }, - arrayOfText: { type: 'text', path: '$.arrayOfText[*]' }, - aStringArray: { type: 'string[]' }, - someStringsAsAnArray: { type: 'string[]', path: '$.someOtherStrings[*]' }, - someOtherStringsAsAnArray: { type: 'string[]', path: '$.someObjects[*].aString' } - }) - }) - - it("doesn't add fields defined in the schema that are missing", () => { - const actual = toRedisJson(schema, {}) - expect(actual).toEqual({}) - }) - - it("leaves data not defined in the schema in place", () => { - const data = { anExistingField: 42, anExistingNestedField: { anotherExistingField: 23 }} - const actual = toRedisJson(schema, data) - expect(actual).toEqual(data) - }) - - it.each([ - - // boolean - ["leaves a true boolean as a boolean", { aBoolean: true }, { aBoolean: true }], - ["leaves a false boolean as a boolean", { aBoolean: false }, { aBoolean: false }], - ["leaves a null boolean as null", { aBoolean: null }, { aBoolean: null }], - ["leaves a pathed boolean as a boolean", { aPathedBoolean: true }, { aPathedBoolean: true }], - ["leaves a nested boolean as a boolean", { aNestededBoolean: { aBoolean: true } }, { aNestededBoolean: { aBoolean: true } }], - ["removes an explicitly undefined boolean", { aBoolean: undefined }, {}], - ["removes an explicitly undefined pathed boolean", { aPathedBoolean: undefined }, {}], - - // number - ["leaves a number as a number", { aNumber: A_NUMBER }, { aNumber: A_NUMBER }], - ["leaves a null number as null", { aNumber: null }, { aNumber: null }], - ["leaves a pathed number as a number", { aPathedNumber: A_NUMBER }, { aPathedNumber: A_NUMBER }], - ["leaves a nested number as a number", { aNestedNumber: { aNumber: A_NUMBER } }, { aNestedNumber: { aNumber: A_NUMBER } }], - ["removes an explicitly undefined number", { aNumber: undefined }, {}], - ["removes an explicitly undefined pathed number", { aPathedNumber: undefined }, {}], - - // date - ["converts a date to an epoch number", { aDate: A_DATE }, { aDate: A_DATE_EPOCH }], - ["converts an ISO date to an epoch number", { aDate: A_DATE_ISO }, { aDate: A_DATE_EPOCH }], - ["leaves an epoch number as an epoch number", { aDate: A_DATE_EPOCH }, { aDate: A_DATE_EPOCH }], - ["leaves a null date as null", { aDate: null }, { aDate: null }], - ["converts a pathed date to an epoch number", { aPathedDate: A_DATE }, { aPathedDate: A_DATE_EPOCH }], - ["converts a nested date to an epoch number", { aNestedDate: { aPathedDate: A_DATE } }, { aNestedDate: { aPathedDate: A_DATE_EPOCH } }], - ["removes an explicitly undefined date", { aDate: undefined }, {}], - ["removes an explicitly undefined pathed date", { aPathedDate: undefined }, {}], - - // point - ["converts a point to a string", { aPoint: A_POINT }, { aPoint: A_POINT_STRING }], - ["leaves a null point as null", { aPoint: null }, { aPoint: null }], - ["converts a pathed point to a string", { aPathedPoint: A_POINT }, { aPathedPoint: A_POINT_STRING }], - ["converts a nested point to a string", { aNestedPoint: { aPoint: A_POINT } }, { aNestedPoint: { aPoint: A_POINT_STRING } }], - ["removes an explicitly undefined point", { aPoint: undefined }, {}], - ["removes an explicitly undefined pathed point", { aPathedPoint: undefined }, {}], - - // string - ["leaves a string as a string", { aString: A_STRING }, { aString: A_STRING }], - ["coerces a number to a string", { aString: A_NUMBER }, { aString: A_NUMBER_STRING }], - ["coerces a boolean to a string", { aString: true }, { aString: 'true' }], - ["leaves a null string as null", { aString: null }, { aString: null }], - ["leaves a pathed string as a string", { aPathedString: A_STRING }, { aPathedString: A_STRING }], - ["coerces a pathed number to a string", { aPathedString: A_NUMBER }, { aPathedString: A_NUMBER_STRING }], - ["coerces a pathed boolean to a string", { aPathedString: true }, { aPathedString: 'true' }], - ["leaves a nested string as a string", { aNestedString: { aString: A_STRING } }, { aNestedString: { aString: A_STRING } }], - ["coerces a nested number to a string", { aNestedString: { aString: A_NUMBER } }, { aNestedString: { aString: A_NUMBER_STRING } }], - ["coerces a nested boolean to a string", { aNestedString: { aString: true } }, { aNestedString: { aString: 'true' } }], - ["removes an explicitly undefined string", { aString: undefined }, {}], - ["removes an explicitly undefined pathed string", { aPathedString: undefined }, {}], - - // text - ["leaves a string as text", { someText: SOME_TEXT }, { someText: SOME_TEXT }], - ["coerces a number to text", { someText: A_NUMBER }, { someText: A_NUMBER_STRING }], - ["coerces a boolean to text", { someText: true }, { someText: 'true' }], - ["leaves null text as null", { someText: null }, { someText: null }], - ["leaves a pathed string as text", { somePathedText: SOME_TEXT }, { somePathedText: SOME_TEXT }], - ["coerces a pathed number to text", { somePathedText: A_NUMBER }, { somePathedText: A_NUMBER_STRING }], - ["coerces a pathed boolean to text", { somePathedText: true }, { somePathedText: 'true' }], - ["leaves a nested string as text", { someNestedText: { someText: A_STRING } }, { someNestedText: { someText: A_STRING } }], - ["coerces a nested number to text", { someNestedText: { someText: A_NUMBER } }, { someNestedText: { someText: A_NUMBER_STRING } }], - ["coerces a nested boolean to text", { someNestedText: { someText: true } }, { someNestedText: { someText: 'true' } }], - ["removes explicitly undefined text", { someText: undefined }, {}], - ["removes explicitly undefined pathed text", { somePathedText: undefined }, {}], - - // string[] - ["leaves a string[] as a string[]", { aStringArray: SOME_STRINGS }, { aStringArray: SOME_STRINGS }], - ["coerces numbers and booleans in a string[] to strings", { aStringArray: [ A_STRING, A_NUMBER, true] }, { aStringArray: [A_STRING, A_NUMBER_STRING, 'true'] }], - ["leaves an empty string[] as a string[]", { aStringArray: [] }, { aStringArray: [] }], - ["leaves a null string[] as null", { aStringArray: null }, { aStringArray: null }], - ["leaves a string[] that doesn't contain a string[] as is", { aStringArray: 'NOT_AN_ARRAY' }, { aStringArray: 'NOT_AN_ARRAY' }], - ["leaves a pathed string[] as a string[]", { aPathedStringArray: SOME_STRINGS }, { aPathedStringArray: SOME_STRINGS }], - ["leaves a nested string[] as a string[]", { aNestedStringArray: { aStringArray: SOME_STRINGS } }, { aNestedStringArray: { aStringArray: SOME_STRINGS } }], - ["removes an explicitly undefined string[]", { aStringArray: undefined }, {}], - ["removes an explicitly undefined pathed string[]", { aPathedStringArray: undefined }, {}], - - // dispersed string[] - ["leaves dispersed string[] as strings", { someOtherStrings: SOME_STRINGS }, { someOtherStrings: SOME_STRINGS }], - ["coerces numbers and booleans in a dispersed string[] to strings", - { someOtherStrings: [ A_STRING, A_NUMBER, true ] }, - { someOtherStrings: [ A_STRING, A_NUMBER_STRING, 'true' ] }], - - // widely dispersed string[] - ["leaves widely dispersed string[] as strings", - { someObjects: [ { aString: A_STRING }, { aString: ANOTHER_STRING }, { aString: A_THIRD_STRING } ] }, - { someObjects: [ { aString: A_STRING }, { aString: ANOTHER_STRING }, { aString: A_THIRD_STRING } ] }], - ["coerces numbers and booleans in a widely dispersed string[] to strings", - { someObjects: [ { aString: A_STRING }, { aString: A_NUMBER }, { aString: true } ] }, - { someObjects: [ { aString: A_STRING }, { aString: A_NUMBER_STRING }, { aString: 'true' } ] }], - ["removes explicity undefined values in a widely dispersed string[]", - { someObjects: [ { aString: A_STRING }, { aString: undefined }, { aString: ANOTHER_STRING } ] }, - { someObjects: [ { aString: A_STRING }, {}, { aString: ANOTHER_STRING }, ] }] - - ])('%s', (_, data, expected) => { - const actual = toRedisJson(schema, data) - expect(actual).not.toBe(data) - expect(actual).toEqual(expected) - }) - - it("complains when given an invalid point", () => { - expect(() => toRedisJson(schema, { aPoint: AN_INVALID_POINT })) - .toThrowErrorOfType(PointOutOfRange, "Points must be between ±85.05112878 latitude and ±180 longitude.") - }) - - it.each([ - - // boolean - ["complains when a boolean is not a boolean", { aBoolean: 'NOT_A_BOOLEAN' }, `Unexpected value for field 'aBoolean' of type 'boolean' in JSON at "$.aBoolean".`], - ["complains when a pathed boolean is not a boolean", { aPathedBoolean: 'NOT_A_BOOLEAN' }, `Unexpected value for field 'anotherBoolean' of type 'boolean' in JSON at "$.aPathedBoolean".`], - ["complains when a nested boolean is not a boolean", { aNestedBoolean: { aBoolean: 'NOT_A_BOOLEAN' } }, `Unexpected value for field 'aNestedBoolean' of type 'boolean' in JSON at "$.aNestedBoolean.aBoolean".`], - ["complains when a pathed boolean points to multiple results", { someBooleans: [ true, false, true ] }, `Unexpected value for field 'someBooleans' of type 'boolean' in JSON at "$.someBooleans[*]".`], - - // number - ["complains when a number is not a number", { aNumber: 'NOT_A_NUMBER' }, `Unexpected value for field 'aNumber' of type 'number' in JSON at "$.aNumber".`], - ["complains when a pathed number is not a number", { aPathedNumber: 'NOT_A_NUMBER' }, `Unexpected value for field 'anotherNumber' of type 'number' in JSON at "$.aPathedNumber".`], - ["complains when a nested number is not a number", { aNestedNumber: { aNumber: 'NOT_A_NUMBER' } }, `Unexpected value for field 'aNestedNumber' of type 'number' in JSON at "$.aNestedNumber.aNumber".`], - ["complains when a pathed number points to multiple results", { someNumbers: [ 42, 23, 13 ] }, `Unexpected value for field 'someNumbers' of type 'number' in JSON at "$.someNumbers[*]".`], - - // date - ["complains when a date is not a date", { aDate: true }, `Unexpected value for field 'aDate' of type 'date' in JSON at "$.aDate".`], - ["complains when a date is not a date string", { aDate: 'NOT_A_DATE' }, `Unexpected value for field 'aDate' of type 'date' in JSON at "$.aDate".`], - ["complains when a pathed date is not a date", { aPathedDate: true }, `Unexpected value for field 'anotherDate' of type 'date' in JSON at "$.aPathedDate".`], - ["complains when a pathed date is not a date string", { aPathedDate: 'NOT_A_DATE' }, `Unexpected value for field 'anotherDate' of type 'date' in JSON at "$.aPathedDate".`], - ["complains when a nested date is not a date", { aNestedDate: { aDate: true } }, `Unexpected value for field 'aNestedDate' of type 'date' in JSON at "$.aNestedDate.aDate".`], - ["complains when a nested date is not a date string", { aNestedDate: { aDate: 'NOT_A_DATE' } }, `Unexpected value for field 'aNestedDate' of type 'date' in JSON at "$.aNestedDate.aDate".`], - ["complains when a pathed date points to multiple results", { someDates: [ A_DATE, A_DATE, A_DATE ] }, `Unexpected value for field 'someDates' of type 'date' in JSON at "$.someDates[*]".`], - - // point - ["complains when a point is not a point", { aPoint: 'NOT_A_POINT' }, `Unexpected value for field 'aPoint' of type 'point' in JSON at "$.aPoint".`], - ["complains when a point is a partial point", { aPoint: A_PARITAL_POINT }, `Unexpected value for field 'aPoint' of type 'point' in JSON at "$.aPoint".`], - ["complains when a pathed point is not a point", { aPathedPoint: 'NOT_A_POINT' }, `Unexpected value for field 'anotherPoint' of type 'point' in JSON at "$.aPathedPoint".`], - ["complains when a pathed point is a partial point", { aPathedPoint: A_PARITAL_POINT }, `Unexpected value for field 'anotherPoint' of type 'point' in JSON at "$.aPathedPoint".`], - ["complains when a nested point is not a point", { aNestedPoint: { aPoint: 'NOT_A_POINT' } }, `Unexpected value for field 'aNestedPoint' of type 'point' in JSON at "$.aNestedPoint.aPoint".`], - ["complains when a nested point is a partial point", { aNestedPoint: { aPoint: A_PARITAL_POINT } }, `Unexpected value for field 'aNestedPoint' of type 'point' in JSON at "$.aNestedPoint.aPoint".`], - ["complains when a pathed point points to multiple results", { somePoints: [ A_POINT, A_POINT, A_POINT ] }, `Unexpected value for field 'somePoints' of type 'point' in JSON at "$.somePoints[*]".`], - - // string - ["complains when a string is not a string", { aString: A_DATE }, `Unexpected value for field 'aString' of type 'string' in JSON at "$.aString".`], - ["complains when a pathed string is not a string", { aPathedString: A_DATE }, `Unexpected value for field 'anotherString' of type 'string' in JSON at "$.aPathedString".`], - ["complains when a nested string is not a string", { aNestedString: { aString: A_DATE } }, `Unexpected value for field 'aNestedString' of type 'string' in JSON at "$.aNestedString.aString".`], - ["complains when a pathed string points to multiple results", { someStrings: SOME_STRINGS }, `Unexpected value for field 'someStrings' of type 'string' in JSON at "$.someStrings[*]".`], - - // text - ["complains when text is not a string", { someText: A_DATE }, `Unexpected value for field 'someText' of type 'text' in JSON at "$.someText".`], - ["complains when pathed text is not a string", { somePathedText: A_DATE }, `Unexpected value for field 'someMoreText' of type 'text' in JSON at "$.somePathedText".`], - ["complains when nested text is not a string", { someNestedText: { someText: A_DATE } }, `Unexpected value for field 'someNestedText' of type 'text' in JSON at "$.someNestedText.someText".`], - ["complains when a pathed text points to multiple results", { arrayOfText: SOME_STRINGS }, `Unexpected value for field 'arrayOfText' of type 'text' in JSON at "$.arrayOfText[*]".`], - - ])('%s', (_, data, expectedMessage) => { - expect(() => toRedisJson(schema, data)).toThrowErrorOfType(InvalidJsonInput, expectedMessage) - }) - - it.each([ - - // string[] - ["complains when a string[] contains null", { aStringArray: [ A_STRING, null, ANOTHER_STRING ] }, `Null or undefined found in field 'aStringArray' of type 'string[]' in JSON at "$.aStringArray[*]".`], - ["complains when a string[] contains undefined", { aStringArray: [ A_STRING, undefined, ANOTHER_STRING] }, `Null or undefined found in field 'aStringArray' of type 'string[]' in JSON at "$.aStringArray[*]".`], - ["complains when a dispersed string[] contains null", { someOtherStrings: [ A_STRING, null, ANOTHER_STRING] }, `Null or undefined found in field 'someStringsAsAnArray' of type 'string[]' in JSON at "$.someOtherStrings[*]".`], - ["complains when a dispersed string[] contains undefined", { someOtherStrings: [ A_STRING, undefined, ANOTHER_STRING] }, `Null or undefined found in field 'someStringsAsAnArray' of type 'string[]' in JSON at "$.someOtherStrings[*]".`], - ["complains when a widely dispersed string[] contains null", { someObjects: [ { aString: A_STRING }, { aString: null }, { aString: ANOTHER_STRING } ] }, `Null or undefined found in field 'someOtherStringsAsAnArray' of type 'string[]' in JSON at "$.someObjects[*].aString".`] - - ])('%s', (_, data, expectedMessage) => { - expect(() => toRedisJson(schema, data)).toThrowErrorOfType(NullJsonInput, expectedMessage) - }) - }) -}) diff --git a/src/client.ts b/src/client.ts new file mode 100644 index 0000000..c4e2ce3 --- /dev/null +++ b/src/client.ts @@ -0,0 +1,134 @@ +import { PrettyError } from "@infinite-fansub/logger"; +import { createClient } from "redis"; + +import { Schema } from "./schema"; +import { Model } from "./model"; + +import type { + ExtractSchemaMethods, + MethodsDefinition, + SchemaDefinition, + SchemaOptions, + WithModules, + NodeRedisClient, + ExtractName, + URLObject, + Module, + ClientOptions +} from "./typings"; + +export class Client = {}> { + #client!: NodeRedisClient; + #models: Map> = new Map(); + #open: boolean = false; + #prefix: string = "Redis-OM"; + + /** Please only access this from within a module or if you know what you are doing */ + public _options: ClientOptions; + + public constructor(options?: ClientOptions) { + this._options = options ?? >{}; + + if (this._options.modules) { + for (let i = 0, len = this._options.modules.length; i < len; i++) { + const module = this._options.modules[i]; + //@ts-expect-error shenanigans + this[module.name] = new module.ctor(this); + } + } + } + + public async connect(url: string | URLObject = this._options.url ?? "redis://localhost:6379"): Promise { + if (this.#open) return this; + + if (typeof url === "object") { + + const { username, password, entrypoint, port } = url; + url = `${username}:${password}@${(/:\d$/).exec(entrypoint) ? entrypoint : `${entrypoint}:${port}`}`; + } + + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + this.#client ??= createClient({ url }); + try { + await this.#client.connect(); + this.#open = true; + } catch (e) { + Promise.reject(e); + } + + return this; + } + + public async disconnect(): Promise { + await this.#client.quit(); + + this.#open = false; + return this; + } + + public async forceDisconnect(): Promise { + await this.#client.disconnect(); + + this.#open = false; + return this; + } + + public schema>(definition: T, methods?: M, options?: SchemaOptions): Schema< + { [K in keyof (T & SD)]: (T & SD)[K] }, + { [K in keyof (M & MD)]: (M & MD)[K] } + > { + return new Schema({ + ...this._options.inject?.schema?.definition, + ...definition + }, { + ...this._options.inject?.schema?.methods, + ...methods + }, { + ...this._options.inject?.schema?.options, + ...options + }); + } + + public model>(name: string, schema?: T): Model & ExtractSchemaMethods { + let model = this.#models.get(name); + if (model) return model; + + if (!schema) throw new PrettyError("You have to pass a schema if it doesn't exist", { + reference: "redis-om" + }); + + model = new Model(this.#client, this.#prefix, "V1", name, schema); + this.#models.set(name, model); + return model; + } + + public withModules>(...modules: ExtractName): this & WithModules { + for (let i = 0, len = modules.length; i < len; i++) { + const module = modules[i]; + //@ts-expect-error shenanigans + this[module.name] = new module.ctor(this); + } + + return this; + } + + public get raw(): NodeRedisClient { + return this.#client; + } + + public get isOpen(): boolean { + return this.#open; + } + + public set redisClient(client: NodeRedisClient) { + if (!this.#open) { + this.#client = client; + } + } + + public set globalPrefix(str: string) { + this.#prefix = str; + } +} + +export const client = new Client(); \ No newline at end of file diff --git a/src/document/document-helpers/general-helpers.ts b/src/document/document-helpers/general-helpers.ts new file mode 100644 index 0000000..3ac5d4b --- /dev/null +++ b/src/document/document-helpers/general-helpers.ts @@ -0,0 +1,161 @@ +import { PrettyError } from "@infinite-fansub/logger"; +import { inspect } from "node:util"; +import { Color } from "colours.js"; + +import type { ObjectField, ParseSchema } from "../../typings"; +import type { HASHDocument } from "../hash-document"; +import type { JSONDocument } from "../json-document"; + +export function dateToNumber(val: Date | string | number): number { + if (val instanceof Date) return val.getTime(); + if (typeof val === "string" || typeof val === "number") return new Date(val).getTime(); + throw new PrettyError("An invalid value was given"); +} + +export function numberToDate(val: number): Date { + return new Date(val); +} + +export function stringsToObject(arr: Array, val: unknown): Record { + let obj: any = {}; + arr.reduce((object, accessor, i) => { + object[accessor] = {}; + + if (arr.length - 1 === i) { + object[accessor] = val; + } + + return >object[accessor]; + }, obj); + + return >obj; +} + +export function validateSchemaReferences( + this: HASHDocument | JSONDocument, + schema: ParseSchema["references"], + data: JSONDocument | ParseSchema["references"] = this +): void { + for (let i = 0, keys = Object.keys(schema), len = keys.length; i < len; i++) { + const key = keys[i]; + const dataVal = data[key]; + + if (typeof dataVal === "undefined") throw new PrettyError(`Found a missing reference: '${key}'`, { + reference: "redis-om" + }); + + for (let j = 0, le = dataVal.length; j < le; j++) { + if (typeof dataVal[i] !== "string") throw new PrettyError("Invalid id inside a reference", { + reference: "redis-om", + lines: [ + { + marker: { text: "Content:" }, + error: `Reference 'ids' must be strings.\nFound type: '${typeof dataVal[i]}'` + } + ] + }); + } + } + +} + +export function validateSchemaData( + this: HASHDocument | JSONDocument, + schema: ParseSchema["data"], + data: HASHDocument | ParseSchema["data"] = this, + isField: boolean = false +): void { + for (let i = 0, entries = Object.entries(schema), len = entries.length; i < len; i++) { + const [key, value] = entries[i]; + + if (isField && typeof data[key] === "undefined") throw new PrettyError(`Found a missing property inside an object: '${key}'`, { + reference: "redis-om" + }); + + const dataVal = data[key]; + + if (dataVal === null) throw new PrettyError("Cannot save 'null' to the database", { + reference: "redis-om" + }); + + if (typeof dataVal === "undefined" && value.optional) continue; + if (typeof dataVal === "undefined" && !value.optional && typeof value.default === "undefined") throw new PrettyError(`'${key}' is required but was not given a value`, { + reference: "redis-om" + }); + + if (value.type === "object") { + if (!(value).properties) continue; + //@ts-expect-error Typescript is getting confused due to the union of array and object + validateSchemaData(value.properties, dataVal, true); + } else if (value.type === "array") { + dataVal.every((val: unknown) => { + if (typeof val === "object") return; + //@ts-expect-error Typescript is getting confused due to the union of array and object + if (value.elements === "text") { + if (typeof val !== "string") throw new PrettyError(`Invalid text received. Expected type: 'string' got '${typeof val}'`, { + reference: "redis-om" + }); + return; + } + //@ts-expect-error Typescript is getting confused due to the union of array and object + if (typeof val !== value.elements) throw new PrettyError(`Got wrong type on array elements. Expected type: '${value.elements}' got '${typeof val}'`, { + reference: "redis-om" + }); + }); + + } else if (value.type === "tuple") { + //@ts-expect-error Typescript is getting confused due to the union of array and object + for (let j = 0, le = value.elements.length; j < le; j++) { + //@ts-expect-error Typescript is getting confused due to the union of array and object + validateSchemaData({ [j]: value.elements[j] }, { [j]: dataVal[j] }); + } + } else if (value.type === "date") { + if (!(dataVal instanceof Date) && typeof dataVal !== "number") throw new PrettyError(`Expected 'Date' or type 'number' but instead got ${typeof dataVal}`, { + reference: "redis-om" + }); + } else if (value.type === "point") { + if (typeof dataVal !== "object") throw new PrettyError("Invalid 'point' format", { + reference: "redis-om", + lines: [ + { + error: inspect({ + longitude: "number", + latitude: "number" + }, { colors: true }), + marker: { text: "Expected:", color: Color.fromHex("#00FF00"), spacedBefore: true, newLine: true } + }, + { + error: typeof dataVal, + marker: { text: "Got: ", color: Color.fromHex("#00FF00"), spacedBefore: true } + } + ] + }); + if (!dataVal.longitude || !dataVal.latitude) throw new PrettyError("'longitude' or 'latitude' where not defined", { + reference: "redis-om" + }); + if (Object.keys(dataVal).length > 2) throw new PrettyError("Invalid 'point' format", { + reference: "redis-om", + lines: [ + { + error: inspect({ + longitude: "number", + latitude: "number" + }, { colors: true }), + marker: { text: "Expected:", color: Color.fromHex("#00FF00"), spacedBefore: true, newLine: true } + }, + { + error: inspect(dataVal, { colors: true }), + marker: { text: "Got:", color: Color.fromHex("#00FF00"), spacedBefore: true, newLine: true } + } + ] + }); + } else if (value.type === "text") { + if (typeof dataVal !== "string") throw new PrettyError("Text field has to be a string"); + } else if (value.type === "vector") { + if (!(dataVal instanceof Float32Array) && !(dataVal instanceof Float64Array) && !Array.isArray(dataVal)) throw new PrettyError("Got wrong vector format"); + } else { + // This handles `number`, `boolean` and `string` types + if (typeof dataVal !== value.type) throw new PrettyError(`Got wrong value type. Expected type: '${value.type}' got '${typeof dataVal}'`); + } + } +} \ No newline at end of file diff --git a/src/document/document-helpers/hash-helpers.ts b/src/document/document-helpers/hash-helpers.ts new file mode 100644 index 0000000..8d37121 --- /dev/null +++ b/src/document/document-helpers/hash-helpers.ts @@ -0,0 +1,173 @@ +import { dateToNumber, numberToDate, stringsToObject } from "./general-helpers"; + +import type { ArrayField, BaseField, ObjectField, Point } from "../../typings"; + +export function booleanToString(val: boolean): string { + return (+val).toString(); +} + +export function pointToString(val: Point): string { + const { longitude, latitude } = val; + return `${longitude},${latitude}`; +} + +export function stringToBoolean(val: string): boolean { + return !!+val; +} + +export function stringToPoint(val: string): Point { + const [longitude, latitude] = val.split(","); + return { longitude: parseFloat(longitude), latitude: parseFloat(latitude) }; +} + +export function stringToNumber(val: string): number { + return parseFloat(val); +} + +export function hashFieldToString(schema: BaseField, val: any): string { + if (schema.type === "boolean") { + return booleanToString(val); + } else if (schema.type === "date") { + return dateToNumber(val).toString(); + } else if (schema.type === "point") { + return pointToString(val); + } else if (schema.type === "array") { + const temp = []; + for (let i = 0, len = (>val).length; i < len; i++) { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + temp.push(hashFieldToString({ type: (schema).elements ?? "string" }, val[i])); + } + return temp.join((schema).separator); + } else if (schema.type === "vector") { + return Buffer.from(val).toString(); + } + + return val.toString(); + +} + +export function objectToHashString(data: Record, k: string, schema?: Record): Array { + const init: Array = []; + for (let i = 0, entries = Object.entries(data), len = entries.length; i < len; i++) { + const [key, val] = entries[i]; + + if (typeof val === "object" && !Array.isArray(val) && !(val instanceof Date)) { + if (typeof schema?.[key]?.properties !== "undefined") { + init.push(...objectToHashString(val, `${k}.${key}`, schema[key].properties)); + continue; + } + init.push(...objectToHashString(val, `${k}.${key}`)); + continue; + } + + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + init.push(`${k}.${key}`, hashFieldToString(schema?.[key] ?? convertUnknownToSchema(val), val) ?? ""); + } + + return init; +} + +export function convertUnknownToSchema(val: any): BaseField { + if (Array.isArray(val)) return { type: "array" }; + if (val instanceof Date) return { type: "date" }; + if (typeof val === "object" && "latitude" in val && "longitude" in val) return { type: "point" }; + return { type: <"string" | "number" | "boolean" | "object">typeof val }; +} + +export function stringToHashField(schema: BaseField, val: string): any { + if (schema.type === "number") { + return stringToNumber(val); + } if (schema.type === "boolean") { + return stringToBoolean(val); + } else if (schema.type === "date") { + return numberToDate(stringToNumber(val)); + } else if (schema.type === "point") { + return stringToPoint(val); + } else if (schema.type === "array") { + const temp = val.split((schema).separator ?? ","); + for (let i = 0, len = temp.length; i < len; i++) { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + temp[i] = stringToHashField({ type: (schema).elements ?? "string" }, temp[i]); + } + return temp; + } else if (schema.type === "vector") { + //@ts-expect-error Type overload + if (schema.vecType === "FLOAT32") return new Float32Array(val); + //@ts-expect-error Type overload + if (schema.vecType === "FLOAT64") return new Float64Array(val); + } + return val; +} + +export function stringToHashArray(arr: Array, schema: any, val: string): Record { + let temp: any = []; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const idx = arr.shift()!; + let props = schema[idx].properties; + + if (arr.length === 1) { + return { [arr[0]]: val }; + } + + for (let i = 0, len = arr.length; i < len; i++) { + const value = arr[i]; + + if (props[value].type === "object") { + temp.push(value); + const x = i + 1; + props = { [arr[x]]: props[value].properties[arr[x]] }; + continue; + } + + temp.push(value, stringToHashField(props[value], val)); + } + + const trueVal = temp.pop(); + return stringsToObject(temp, trueVal); +} + +export function deepMerge(...objects: Array>): Record { + let newObject: Record = {}; + + for (let i = 0, len = objects.length; i < len; i++) { + const obj = objects[i]; + + if (typeof obj === "undefined") continue; + + for (let j = 0, entries = Object.entries(obj), le = entries.length; j < le; j++) { + const [key, value] = entries[j]; + if (typeof value === "object" && value) { + const derefObj = { ...value }; + + newObject[key] = newObject[key] ? deepMerge(newObject[key], derefObj) : derefObj; + } else { + newObject[key] = value; + } + } + } + + return newObject; +} + +export function getLastKeyInSchema(data: Required, key: string): BaseField | undefined { + if (!data.properties) return { type: "string" }; + for (let i = 0, entries = Object.entries(data.properties), len = entries.length; i < len; i++) { + const [k, value] = entries[i]; + + if (key === k) { + return value; + } + + if (typeof value === "undefined") continue; + + //@ts-expect-error I dont have a proper type for this + if (value.type === "object") { + //@ts-expect-error I dont have a proper type for this + return getLastKeyInSchema(value, key); + } + + continue; + } + + return void 0; +} \ No newline at end of file diff --git a/src/document/document-helpers/index.ts b/src/document/document-helpers/index.ts new file mode 100644 index 0000000..8559e4e --- /dev/null +++ b/src/document/document-helpers/index.ts @@ -0,0 +1,3 @@ +export * from "./hash-helpers"; +export * from "./json-helpers"; +export * from "./general-helpers"; \ No newline at end of file diff --git a/src/document/document-helpers/json-helpers.ts b/src/document/document-helpers/json-helpers.ts new file mode 100644 index 0000000..7830f57 --- /dev/null +++ b/src/document/document-helpers/json-helpers.ts @@ -0,0 +1,122 @@ +import { dateToNumber, numberToDate } from "./general-helpers"; + +import type { ArrayField, BaseField, ObjectField } from "../../typings"; + +export function jsonFieldToDoc(schema: BaseField, val: any): any { + if (schema.type === "date") { + return numberToDate(val); + } else if (schema.type === "point") { + const [longitude, latitude] = val.split(","); + return { longitude: +longitude, latitude: +latitude }; + } else if (schema.type === "object") { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + parseJsonObject(schema, val); + } else if (schema.type === "array") { + for (let i = 0, le = val.length; i < le; i++) { + val[i] = jsonFieldToDoc({ type: (schema).elements }, val[i]); + } + return val; + } else if (schema.type === "vector") { + //@ts-expect-error Type overload + if (schema.vecType === "FLOAT32") return new Float32Array(val); + //@ts-expect-error Type overload + if (schema.vecType === "FLOAT64") return new Float64Array(val); + } + + return val; +} + +export function docToJson(schema: BaseField, val: any): any { + if (schema.type === "date") { + return dateToNumber(val); + } else if (schema.type === "point") { + return `${val.longitude},${val.latitude}`; + } else if (schema.type === "object") { + parseDoc(schema, val); + } else if (schema.type === "array") { + for (let i = 0, le = val.length; i < le; i++) { + val[i] = docToJson({ type: (schema).elements }, val[i]); + } + return val; + } else if (schema.type === "vector") { + return Array.from(val); + } + + return val; + +} + +export function parseDoc(schema: Required, val: any): any { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + for (let i = 0, entries = Object.entries((schema).properties!), len = entries.length; i < len; i++) { + const [key, value] = entries[i]; + + //@ts-expect-error I dont have a proper type for this + if (value.type === "object") { + //@ts-expect-error I dont have a proper type for this + val[key] = parseDoc(value, val[key]); + } + + //@ts-expect-error I dont have a proper type for this + val[key] = docToJson(value, val[key]); + } + + return val; +} + +export function parseJsonObject(schema: Required, val: any): any { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + for (let i = 0, entries = Object.entries((schema).properties!), len = entries.length; i < len; i++) { + const [key, value] = entries[i]; + + //@ts-expect-error I dont have a proper type for this + if (value.type === "object") { + //@ts-expect-error I dont have a proper type for this + val[key] = parseJsonObject(value, val[key]); + } + + //@ts-expect-error I dont have a proper type for this + val[key] = jsonFieldToDoc(value, val[key]); + } + + return val; +} + +export function objectToString(val: Record, k: string): Array> { + const arr: Array> = []; + + for (let i = 0, entries = Object.entries(val), len = entries.length; i < len; i++) { + const [key, value] = entries[i]; + + if (typeof value === "object") { + const temp = objectToString(value, `${k}.${key}`); + arr.push(...temp); + continue; + } + + arr.push({ [`${k}.${key}`]: value }); + } + + return arr; +} + +export function tupleToObjStrings(val: Array, key: string): Array> { + const arr: Array> = []; + + for (let i = 0, len = val.length; i < len; i++) { + const value = val[i]; + + if (typeof value === "object") { + for (let j = 0, entries = Object.entries(value), le = entries.length; j < le; j++) { + const [k, v] = entries[j]; + + arr.push({ [`${key}.${i}.${k}`]: v }); + } + continue; + } + + arr.push({ [`${key}.${i}`]: value }); + } + + return arr; +} \ No newline at end of file diff --git a/src/document/hash-document.ts b/src/document/hash-document.ts new file mode 100644 index 0000000..52f4715 --- /dev/null +++ b/src/document/hash-document.ts @@ -0,0 +1,209 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +import { randomUUID } from "node:crypto"; + +import { ReferenceArray } from "../utils"; +import { + validateSchemaReferences, + validateSchemaData, + objectToHashString, + getLastKeyInSchema, + tupleToObjStrings, + hashFieldToString, + stringToHashField, + stringToHashArray, + stringsToObject, + deepMerge +} from "./document-helpers"; + +import type { DocumentShared, ObjectField, ParseSchema } from "../typings"; + +export class HASHDocument implements DocumentShared { + + readonly #schema: ParseSchema; + readonly #validate: boolean; + readonly #autoFetch: boolean; + #validateSchemaReferences = validateSchemaReferences; + #validateSchemaData = validateSchemaData; + + public readonly $global_prefix: string; + public readonly $prefix: string; + public readonly $model_name: string; + public readonly $suffix: string | undefined; + public readonly $id: string; + public readonly $record_id: string; + + /* + * Using any so everything works as intended + * I couldn't find any other way to do this or implement the MapSchema type directly in the class + */ + /** @internal */ + [key: string]: any; + + public constructor( + schema: ParseSchema, + record: { + globalPrefix: string, + prefix: string, + name: string, + suffix?: string | (() => string) | undefined, + id?: string | undefined + }, + data?: Record, + isFetchedData: boolean = false, + validate: boolean = true, + wasAutoFetched: boolean = false + ) { + this.$global_prefix = record.globalPrefix; + this.$prefix = record.prefix; + this.$model_name = record.name; + this.$suffix = data?.$suffix ?? (typeof record.suffix === "function" ? record.suffix() : record.suffix); + this.$id = data?.$id ?? record.id ?? randomUUID(); + this.$record_id = `${this.$global_prefix}:${this.$prefix}:${this.$model_name}:${this.$suffix ? `${this.$suffix}:` : ""}${this.$id}`; + this.#schema = schema; + this.#validate = validate; + this.#autoFetch = wasAutoFetched; + + this.#populate(); + + if (data) { + if (isFetchedData) { + for (let i = 0, entries = Object.entries(data), len = entries.length; i < len; i++) { + const [key, value] = entries[i]; + if (key.startsWith("$")) continue; + const arr = key.split("."); + + if (arr.length > 1) /* This is an object or tuple */ { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (schema.data[arr[0]]?.type === "tuple") { + // var name + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const temp = arr.shift()!; + + if (arr.length === 1) { + this[temp].push(stringToHashField( + //@ts-expect-error Type overload + schema.data[temp].elements[arr[0]], + value + )); + + continue; + } + + //@ts-expect-error Type overload + this[temp].push(stringToHashArray(arr, schema.data[temp].elements, value)); + } else /*we assume its an object*/ { + this[arr[0]] = deepMerge( + this[arr[0]], + stringsToObject( + arr, + stringToHashField( + getLastKeyInSchema(>schema.data[arr[0]], arr.at(-1)) ?? { type: "string" }, + value + ) + )[arr[0]] + ); + } + continue; + } + + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (schema.references[key] === null) { + if (!this.#autoFetch) { + this[key] = new ReferenceArray(...>stringToHashField({ type: "array" }, value)); + continue; + } + this[key] = value; + continue; + } + + this[key] = stringToHashField(schema.data[key], value); + } + } else { + for (let i = 0, entries = Object.entries(data), len = entries.length; i < len; i++) { + const [key, value] = entries[i]; + if (key.startsWith("$")) continue; + this[key] = value; + } + } + } + } + + #populate(): void { + for (let i = 0, entries = Object.entries(this.#schema.data), len = entries.length; i < len; i++) { + const [key, value] = entries[i]; + this[key] = value.default ?? (value.type === "object" + ? {} + : value.type === "tuple" + ? [] + : value.type === "vector" + //@ts-expect-error Type overload + ? value.vecType === "FLOAT32" + ? new Float32Array() + //@ts-expect-error Type overload + : value.vecType === "FLOAT64" + ? new Float64Array() + : [] + : void 0); + } + + for (let i = 0, keys = Object.keys(this.#schema.references), len = keys.length; i < len; i++) { + const key = keys[i]; + this[key] = new ReferenceArray(); + } + } + + public toString(): string { + if (this.#validate) this.#validateSchemaData(this.#schema.data); + + const arr = [ + "$id", + this.$id + ]; + + if (this.$suffix) arr.push("$suffix", this.$suffix); + + for (let i = 0, entries = Object.entries(this.#schema.data), len = entries.length; i < len; i++) { + const [key, val] = entries[i]; + + if (typeof this[key] === "undefined") continue; + + if (val.type === "object") { + //@ts-expect-error Typescript is getting confused due to the union of array and object + arr.push(...objectToHashString(this[key], key, val.properties)); + continue; + } else if (val.type === "tuple") { + const temp = tupleToObjStrings(this[key], key); + for (let j = 0, le = temp.length; j < le; j++) { + const [k, value] = Object.entries(temp[j])[0]; + + //@ts-expect-error Type Overload + if (val.elements[j].type === "object") { + //@ts-expect-error Type Overload + arr.push(...objectToHashString(value, k, val.elements[j].properties)); + continue; + } + + arr.push(k, hashFieldToString(val, value)); + } + continue; + } + + arr.push(key, hashFieldToString(val, this[key])); + + } + + if (!this.#autoFetch) { + if (this.#validate) this.#validateSchemaReferences(this.#schema.references); + for (let i = 0, keys = Object.keys(this.#schema.references), len = keys.length; i < len; i++) { + const key = keys[i]; + + if (!this[key]?.length) continue; + arr.push(key, hashFieldToString({ type: "array" }, this[key])); + } + } + + //@ts-expect-error pls dont question it + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return arr; + } +} \ No newline at end of file diff --git a/src/document/index.ts b/src/document/index.ts new file mode 100644 index 0000000..b4b186f --- /dev/null +++ b/src/document/index.ts @@ -0,0 +1,2 @@ +export * from "./json-document"; +export * from "./hash-document"; \ No newline at end of file diff --git a/src/document/json-document.ts b/src/document/json-document.ts new file mode 100644 index 0000000..24fcead --- /dev/null +++ b/src/document/json-document.ts @@ -0,0 +1,179 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +import { randomUUID } from "node:crypto"; + +import { ReferenceArray } from "../utils"; +import { + validateSchemaReferences, + validateSchemaData, + tupleToObjStrings, + jsonFieldToDoc, + docToJson +} from "./document-helpers"; + +import type { DocumentShared, ParseSchema } from "../typings"; + +export class JSONDocument implements DocumentShared { + + readonly #schema: ParseSchema; + readonly #validate: boolean; + readonly #autoFetch: boolean; + #validateSchemaReferences = validateSchemaReferences; + #validateSchemaData = validateSchemaData; + + public readonly $global_prefix: string; + public readonly $prefix: string; + public readonly $model_name: string; + public readonly $suffix: string | undefined; + public readonly $id: string; + public readonly $record_id: string; + + /* + * Using any so everything works as intended + * I couldn't find any other way to do this or implement the MapSchema type directly in the class + */ + /** @internal */ + [key: string]: any; + + public constructor( + schema: ParseSchema, + record: { + globalPrefix: string, + prefix: string, + name: string, + suffix?: string | (() => string) | undefined, + id?: string | undefined + }, + data?: Record, + isFetchedData: boolean = false, + validate: boolean = true, + wasAutoFetched: boolean = false + ) { + this.$global_prefix = record.globalPrefix; + this.$prefix = record.prefix; + this.$model_name = record.name; + this.$suffix = data?.$suffix ?? (typeof record.suffix === "function" ? record.suffix() : record.suffix); + this.$id = data?.$id ?? record.id ?? randomUUID(); + this.$record_id = `${this.$global_prefix}:${this.$prefix}:${this.$model_name}:${this.$suffix ? `${this.$suffix}:` : ""}${this.$id}`; + this.#schema = schema; + this.#validate = validate; + this.#autoFetch = wasAutoFetched; + + this.#populate(); + + if (data) { + if (isFetchedData) { + for (let i = 0, entries = Object.entries(data), len = entries.length; i < len; i++) { + const [key, value] = entries[i]; + if (key.startsWith("$")) continue; + const arr = key.split("."); + + if (arr.length > 1) /* This is a tuple */ { + + // var name + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const temp = arr.shift()!; + + if (arr.length === 1) { + //@ts-expect-error Type overload + this[temp].push(jsonFieldToDoc(schema.data[temp].elements[arr[0]], value)); + continue; + } + + //@ts-expect-error Type overload + this[temp].push({ [arr[1]]: jsonFieldToDoc(schema.data[temp].elements[arr[0]].properties[arr[1]], value) }); + continue; + } + + if (typeof schema.data[key] !== "undefined") { + this[key] = jsonFieldToDoc(schema.data[key], value); + continue; + } + + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (schema.references[key] === null && !this.#autoFetch) { + this[key] = new ReferenceArray(...>value); + continue; + } + + this[key] = value; + } + } else { + for (let i = 0, entries = Object.entries(data), len = entries.length; i < len; i++) { + const [key, value] = entries[i]; + if (key.startsWith("$")) continue; + this[key] = value; + } + } + } + } + + #populate(): void { + for (let i = 0, entries = Object.entries(this.#schema.data), len = entries.length; i < len; i++) { + const [key, value] = entries[i]; + this[key] = value.default ?? (value.type === "object" + ? {} + : value.type === "tuple" + ? [] + : value.type === "vector" + //@ts-expect-error Type overload + ? value.vecType === "FLOAT32" + ? new Float32Array() + //@ts-expect-error Type overload + : value.vecType === "FLOAT64" + ? new Float64Array() + : [] + : void 0); + } + + for (let i = 0, keys = Object.keys(this.#schema.references), len = keys.length; i < len; i++) { + const key = keys[i]; + this[key] = new ReferenceArray(); + } + } + + public toString(): string { + if (this.#validate) this.#validateSchemaData(this.#schema.data); + + const obj: Record = { + $suffix: this.$suffix, + $id: this.$id + }; + + for (let i = 0, entries = Object.entries(this.#schema.data), len = entries.length; i < len; i++) { + const [key, val] = entries[i]; + + if (typeof this[key] === "undefined") continue; + + if (val.type === "tuple") { + const temp = tupleToObjStrings(this[key], key); + for (let j = 0, le = temp.length; j < le; j++) { + const [k, value] = Object.entries(temp[j])[0]; + + //@ts-expect-error Type overload + if (val.elements[j].type === "object") { + //@ts-expect-error Type overload + for (let u = 0, en = Object.entries(val.elements[j].properties), l = en.length; u < l; u++) { + const objV = en[u][1]; + obj[k] = docToJson(objV, value); + } + continue; + } + + obj[k] = value; + } + continue; + } + + obj[key] = docToJson(val, this[key]); + } + + if (!this.#autoFetch) { + if (this.#validate) this.#validateSchemaReferences(this.#schema.references); + for (let i = 0, keys = Object.keys(this.#schema.references), len = keys.length; i < len; i++) { + const key = keys[i]; + obj[key] = this[key]; + } + } + return JSON.stringify(obj, null); + } +} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..e06f048 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,3 @@ +export type * from "./model"; +export * from "./client"; +export * from "./schema"; \ No newline at end of file diff --git a/src/model.ts b/src/model.ts new file mode 100644 index 0000000..de3245c --- /dev/null +++ b/src/model.ts @@ -0,0 +1,345 @@ +import { PrettyError } from "@infinite-fansub/logger"; +import { createHash } from "node:crypto"; + +import { stringToHashField } from "./document/document-helpers"; +import { JSONDocument, HASHDocument } from "./document"; +import { methods, schemaData } from "./utils/symbols"; +import { parseSchemaToSearchIndex } from "./utils"; +import { Search } from "./search/search"; + +import type { Schema } from "./schema"; +import type { + ExtractParsedSchemaDefinition, + ReturnDocument, + NodeRedisClient, + ModelOptions, + VectorField, + MapSchema, + ParsedMap, + Doc +} from "./typings"; + +export class Model> { + readonly #schema: S; + readonly #client: NodeRedisClient; + readonly #searchIndexName: string; + readonly #searchIndexHashName: string; + readonly #searchIndex: Array; + readonly #searchIndexHash: string; + readonly #parsedSchema: ParsedMap; + // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents + readonly #docType: typeof JSONDocument | typeof HASHDocument; + + #options: ModelOptions; + + public constructor(client: NodeRedisClient, globalPrefix: string, ver: string, private readonly name: string, data: S) { + this.#client = client; + this.#schema = data; + this.#options = { + ...this.#schema.options, + skipDocumentValidation: !this.#schema.options.skipDocumentValidation, + globalPrefix, + prefix: this.#schema.options.prefix ?? ver, + version: ver + }; + + this.#parsedSchema = parseSchemaToSearchIndex(this.#schema[schemaData].data); + this.#searchIndexName = `${globalPrefix}:${this.#options.prefix}:${this.name}:index`; + this.#searchIndexHashName = `${globalPrefix}:${this.#options.prefix}:${this.name}:index:hash`; + this.#searchIndex = [ + "FT.CREATE", + this.#searchIndexName, + "ON", + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + data.options.dataStructure!, + "PREFIX", + "1", + `${globalPrefix}:${this.#options.prefix}:${this.name}:`, + "SCHEMA" + ]; + this.#searchIndexHash = createHash("sha1").update(JSON.stringify({ + name, + structure: this.#options.dataStructure, + definition: this.#schema[schemaData].data + })).digest("base64"); + + this.#defineMethods(); + + if (data.options.dataStructure === "HASH") this.#docType = HASHDocument; + else this.#docType = JSONDocument; + } + + public async get(id: string | number, autoFetch?: F): Promise | null> { + if (typeof id === "undefined") throw new PrettyError("A valid id was not given", { + reference: "redis-om" + }); + if (id.toString().split(":").length === 1) { + const suffix = this.#options.suffix; + + if (typeof suffix === "function") { + throw new PrettyError("Due to the use of dynamic suffixes you gave to pass in a full id", { + reference: "redis-om" + }); + } + // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions + id = `${this.#options.globalPrefix}:${this.#options.prefix}:${this.name}:${suffix ? `${suffix}:` : ""}${id}`; + } + + const data = this.#options.dataStructure === "JSON" ? await this.#client.json.get(id.toString()) : await this.#client.hGetAll(id.toString()); + + if (data === null) return null; + if (autoFetch) { + for (let i = 0, keys = Object.keys(this.#schema[schemaData].references), len = keys.length; i < len; i++) { + const key = keys[i]; + //@ts-expect-error node-redis types decided to die + const val = this.#options.dataStructure === "JSON" ? data[key] : stringToHashField({ type: "array" }, data[key]); + const temp = []; + + for (let j = 0, le = val.length; j < le; j++) { + temp.push(this.get(val[j])); + } + + //@ts-expect-error node-redis types decided to die + data[key] = await Promise.all(temp); + } + } + + return new this.#docType(this.#schema[schemaData], { + globalPrefix: this.#options.globalPrefix, + prefix: this.#options.prefix, + name: this.name + }, data, true, this.#options.skipDocumentValidation, autoFetch); + } + + public create(id?: string | number): ReturnDocument; + public create(data?: { $id?: string | number } & MapSchema, true, true>): ReturnDocument; + public create(idOrData?: string | number | { $id?: string | number } & MapSchema, true, true>): ReturnDocument { + if (typeof idOrData === "object") { + return new this.#docType(this.#schema[schemaData], { + globalPrefix: this.#options.globalPrefix, + prefix: this.#options.prefix, + name: this.name, + suffix: this.#options.suffix + }, idOrData, false, this.#options.skipDocumentValidation, false); + } + + return new this.#docType(this.#schema[schemaData], { + globalPrefix: this.#options.globalPrefix, + prefix: this.#options.prefix, + name: this.name, + suffix: this.#options.suffix, + id: idOrData?.toString() + }, void 0, false, this.#options.skipDocumentValidation, false); + } + + public async save(doc: Doc): Promise { + if (typeof doc === "undefined") throw new PrettyError("No document was passed to be save", { + reference: "redis-om" + }); + + // eslint-disable-next-line @typescript-eslint/no-base-to-string + if (this.#options.dataStructure === "HASH") await this.#client.sendCommand(["HSET", doc.$record_id, ...doc.toString()]); + // eslint-disable-next-line @typescript-eslint/no-base-to-string + else await this.#client.sendCommand(["JSON.SET", doc.$record_id, "$", doc.toString()]); + } + + public async delete(...docs: Array): Promise { + if (!docs.length) throw new PrettyError("No documents were given to delete", { + reference: "redis-om" + }); + await this.#client.del(this.#stringOrDocToString(docs)); + } + + public async exists(...docs: Array): Promise { + if (!docs.length) throw new PrettyError("No documents were given to check", { + reference: "redis-om" + }); + return await this.#client.exists(this.#stringOrDocToString(docs)); + } + + public async expire(docs: Array, seconds: number | Date, mode?: "NX" | "XX" | "GT" | "LT"): Promise { + if (!docs.length) throw new PrettyError("No documents were given to expire", { + reference: "redis-om" + }); + docs = this.#stringOrDocToString(docs); + + if (seconds instanceof Date) seconds = Math.round((seconds.getTime() - Date.now()) / 1000); + + const temp = []; + + for (let i = 0, len = docs.length; i < len; i++) { + const doc = docs[i]; + temp.push(this.#client.expire(doc, seconds, mode)); + } + + await Promise.all(temp); + } + + public async createAndSave(data: { $id?: string | number } & MapSchema, true, true>): Promise { + const doc = new this.#docType(this.#schema[schemaData], { + globalPrefix: this.#options.globalPrefix, + prefix: this.#options.prefix, + name: this.name, + suffix: this.#options.suffix + }, data, false, this.#options.skipDocumentValidation, false); + await this.save(doc); + } + + public search(): Search> { + // eslint-disable-next-line max-len + return new Search>(this.#client, this.#schema[schemaData], this.#parsedSchema, { + ...this.#options, + modelName: this.name, + searchIndex: this.#searchIndexName + }); + } + + public async createIndex(): Promise { + const currentIndexHash = await this.#client.get(this.#searchIndexHashName); + if (currentIndexHash === this.#searchIndexHash) return; + + await this.deleteIndex(); + + const prefix = this.#options.dataStructure === "JSON" ? "$." : ""; + + for (let i = 0, len = this.#parsedSchema.size, entries = [...this.#parsedSchema.entries()]; i < len; i++) { + const [key, val] = entries[i]; + const { path, value } = val; + let arrayPath = ""; + + if (this.#options.dataStructure === "JSON") { + if (value.type === "array") { + if (typeof value.elements !== "string") { + throw new PrettyError("Object definitions on `array` are not yet supported by the parser", { + reference: "redis-om" + }); + } + + arrayPath = value.elements === "text" ? "[*]" : value.elements === "number" || value.elements === "date" || value.elements === "point" ? "" : "*"; + } + } + + this.#searchIndex.push( + `${prefix}${key}${arrayPath}`, + "AS", + path, + value.type === "text" + ? "TEXT" + : value.type === "number" || value.type === "date" + ? "NUMERIC" + : value.type === "point" + ? "GEO" + : value.type === "vector" + ? "VECTOR" + : "TAG" + ); + + if (value.type === "vector") { + this.#searchIndex.push( + value.algorithm, + this.#getCount(value), + "TYPE", + value.vecType, + "DIM", + value.dim.toString(), + "DISTANCE_METRIC", + value.distance + ); + + if (value.cap) this.#searchIndex.push("INITIAL_CAP", value.cap.toString()); + + if (value.algorithm === "FLAT") { + if (value.size) this.#searchIndex.push("BLOCK_SIZE", value.size.toString()); + } else { + if (value.m) this.#searchIndex.push("M", value.m.toString()); + if (value.construction) this.#searchIndex.push("EF_CONSTRUCTION", value.construction.toString()); + if (value.runtime) this.#searchIndex.push("EF_RUNTIME", value.runtime.toString()); + if (value.epsilon) this.#searchIndex.push("EPSILON", value.epsilon.toString()); + } + } + + if (value.sortable) this.#searchIndex.push("SORTABLE"); + } + + await Promise.all([ + this.#client.set(this.#searchIndexHashName, this.#searchIndexHash), + this.#client.sendCommand(this.#searchIndex) + ]); + } + + public async deleteIndex(): Promise { + try { + await Promise.all([ + this.#client.unlink(this.#searchIndexHashName), + this.#client.ft.dropIndex(this.#searchIndexName) + ]); + } catch (e) { + if (e instanceof Error && e.message === "Unknown Index name") { + Promise.resolve(); + } else throw e; + } + } + + public async rawSearch(...args: Array): Promise> { + return await this.#client.ft.search(this.#searchIndexName, args.join(" ")); + } + + #getCount(value: VectorField): string { + let count = 6; + + if (value.cap) count += 2; + if (value.algorithm === "FLAT") { + if (value.size) count += 2; + } else { + if (value.m) count += 2; + if (value.construction) count += 2; + if (value.runtime) count += 2; + if (value.epsilon) count += 2; + } + + return count.toString(); + } + + #stringOrDocToString(stringOrNumOrDoc: Array): Array { + const temp = []; + + for (let i = 0, len = stringOrNumOrDoc.length; i < len; i++) { + const el = stringOrNumOrDoc[i]; + let id = ""; + + if (el instanceof JSONDocument || el instanceof HASHDocument) { + id = el.$record_id; + } else if (el.toString().split(":").length === 1) { + const suffix = this.#options.suffix; + + if (typeof suffix === "function") { + throw new PrettyError("Due to the use of dynamic suffixes you gave to pass in a full id", { + reference: "redis-om" + }); + } + // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions + id = `${this.#options.globalPrefix}:${this.#options.prefix}:${this.name}:${suffix ? `${suffix}:` : ""}${el.toString()}`; + } + + temp.push(id); + } + + return temp; + } + + #defineMethods(): void { + for (let i = 0, entries = Object.entries(this.#schema[methods]), len = entries.length; i < len; i++) { + const [key, value] = entries[i]; + //@ts-expect-error Pending fix on type notations + this[key] = value; + } + } + + public get getOptions(): ModelOptions { + return this.#options; + } + + public set mutateOptions(options: Partial) { + this.#options = { ...this.#options, ...options }; + } +} \ No newline at end of file diff --git a/src/schema.ts b/src/schema.ts new file mode 100644 index 0000000..78f1017 --- /dev/null +++ b/src/schema.ts @@ -0,0 +1,225 @@ +import { PrettyError } from "@infinite-fansub/logger"; +import { inspect } from "node:util"; +import { Color } from "colours.js"; + +import { methods, schemaData } from "./utils/symbols"; + +import type { + MethodsDefinition, + SchemaDefinition, + SchemaOptions, + ParseSchema, + BaseField +} from "./typings"; + +export class Schema = MethodsDefinition, P extends ParseSchema = ParseSchema> { + + /** @internal */ + public [methods]: M; + + /** @internal */ + public [schemaData]: P; + + public constructor(rawData: S, methodsData?: M, public readonly options: SchemaOptions = {}) { + this[schemaData] = this.#parse(rawData); + this[methods] = methodsData ?? {}; + this.options.dataStructure = options.dataStructure ?? "JSON"; + + } + + #parse(schema: T): P { + const data: Record = {}; + const references: Record = {}; + + for (let i = 0, entries = Object.entries(schema), len = entries.length; i < len; i++) { + let [key, value] = entries[i]; + if (key.startsWith("$")) throw new PrettyError("Keys cannot start with '$'", { + reference: "redis-om" + }); + + if (typeof value === "string") { + //@ts-expect-error Some people do not read docs + if (value === "object") { + throw new PrettyError("Type 'object' needs to use its object definition", { + reference: "redis-om", + lines: [ + { + error: inspect({ [key]: value }, { colors: true }), + marker: { text: "Parsing:" } + }, + { + error: inspect({ + [key]: { + type: "object" + } + }, { colors: true, compact: false }), + marker: { text: "Fix:", color: Color.fromHex("#00FF00"), newLine: true } + }, + { + error: inspect({ + artist: { + type: "object", + properties: { + name: "string", + age: "number", + hobbies: "array" + } + } + }, { colors: true }), + marker: { text: "Information:", color: Color.fromHex("#009dff"), spacedBefore: true, newLine: true } + } + ] + }); + //@ts-expect-error Some people do not read docs + } else if (value === "reference") { + throw new PrettyError("Type 'reference' needs to use its object definition", { + reference: "redis-om", + lines: [ + { + error: inspect({ [key]: value }, { colors: true }), + marker: { text: "Parsing:" } + }, + { + error: inspect({ + [key]: { + type: "reference", + schema: "`SchemaInstance`" + } + }, { colors: true, compact: false }), + marker: { text: "Fix:", color: Color.fromHex("#00FF00"), newLine: true } + } + ] + }); + //@ts-expect-error Some people do not read docs + } else if (value === "tuple") { + throw new PrettyError("Type 'tuple' needs to use its object definition"); + } else if (value === "array") { + value = { type: value, elements: "string", default: undefined, optional: false, sortable: false, index: true }; + } else if (value === "vector") { + value = { + type: value, + algorithm: "FLAT", + vecType: "FLOAT32", + dim: 128, + distance: "L2", + default: undefined, + optional: false, + sortable: false, + index: true + }; + } else { + value = { type: value, default: undefined, optional: false, sortable: false, index: true }; + } + + data[key] = value; + continue; + } + + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (!value.type) throw new PrettyError("Type not defined", { + reference: "redis-om", + lines: [ + { + error: inspect({ [key]: value }, { colors: true }), + marker: { text: "Parsing:" } + } + ] + }); + + if (value.type === "array") { + if (typeof value.elements === "undefined") value.elements = "string"; + if (typeof value.separator === "undefined") value.separator = ","; + if (typeof value.elements === "object") { + value.elements = this.#parse(value.elements).data; + if (!this.options.noLogs) console.log(`'${key}' will not be indexed because array of objects is not yet supported on RediSearch`); + } + value = this.#fill(value); + } else if (value.type === "date") { + if (value.default instanceof Date) value.default = value.default.getTime(); + if (typeof value.default === "string" || typeof value.default === "number") value.default = new Date(value.default).getTime(); + value = this.#fill(value); + } else if (value.type === "object") { + if (typeof value.default === "undefined") value.default = undefined; + if (typeof value.optional === "undefined") value.optional = false; + if (!value.properties) value.properties = undefined; + else value.properties = this.#parse(value.properties).data; + } else if (value.type === "tuple") { + if (typeof value.elements === "undefined") throw new PrettyError("Tuple needs to have at least 1 element", { + reference: "redis-om" + }); + for (let j = 0, le = value.elements.length; j < le; j++) { + //@ts-expect-error No comment + value.elements[j] = this.#parse({ [j]: typeof value.elements[j] === "string" ? value.elements[j] : { type: "object", properties: value.elements[j] } }).data[j]; + } + value = this.#fill(value); + } else if (value.type === "reference") { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition, @typescript-eslint/strict-boolean-expressions + if (!value.schema) throw new PrettyError("Type 'reference' lacks a schema", { + reference: "redis-om", + lines: [ + { + error: inspect({ [key]: value }, { colors: true }), + marker: { text: "Parsing:" } + }, + { + error: inspect({ + [key]: { + type: "reference", + schema: "`SchemaInstance`" + } + }, { colors: true, compact: false }), + marker: { text: "Fix:", color: Color.fromHex("#00FF00"), newLine: true } + } + ] + }); + references[key] = null; + continue; + } else if (value.type === "vector") { + if (typeof value.algorithm === "undefined") { + throw new PrettyError("'algorithm' is missing on the vector definition", { + reference: "redis-om" + }); + } + if (typeof value.distance === "undefined") { + throw new PrettyError("'distance' is missing on the vector definition", { + reference: "redis-om" + }); + } + if (typeof value.vecType === "undefined") { + throw new PrettyError("'vecType' is missing on the vector definition", { + reference: "redis-om" + }); + } + if (typeof value.dim === "undefined") { + throw new PrettyError("'dim' is missing on the vector definition", { + reference: "redis-om" + }); + } + + if (typeof value.cap === "undefined") value.cap = undefined; + if (value.algorithm === "FLAT") { + if (typeof value.size === "undefined") value.size = undefined; + } else { + if (typeof value.m === "undefined") value.m = undefined; + if (typeof value.construction === "undefined") value.construction = undefined; + if (typeof value.runtime === "undefined") value.runtime = undefined; + if (typeof value.epsilon === "undefined") value.epsilon = undefined; + } + value = this.#fill(value); + } else { + value = this.#fill(value); + } + + data[key] = value; + } + return { data, references }; + } + + #fill(value: BaseField): any { + if (typeof value.default === "undefined") value.default = undefined; + if (typeof value.optional === "undefined") value.optional = false; + if (typeof value.sortable === "undefined") value.sortable = false; + if (typeof value.index === "undefined") value.index = true; + return value; + } +} \ No newline at end of file diff --git a/src/search/search-builders/base.ts b/src/search/search-builders/base.ts new file mode 100644 index 0000000..4c6aff6 --- /dev/null +++ b/src/search/search-builders/base.ts @@ -0,0 +1,46 @@ +import type { ParseSchema } from "../../typings"; +import type { Search } from "../search"; + +export abstract class SearchField> { + + protected negated: boolean = false; + protected value: unknown; + public or: Array = []; + + public constructor(protected search: Search, protected field: string) { } + + // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents + public abstract eq(value: Array | unknown): Search; + + /** Syntactic sugar, calls `eq` */ + public abstract equals(value: unknown): Search; + + /** Syntactic sugar, calls `eq` */ + public abstract equalsTo(value: unknown): Search; + + /** Syntactic sugar, return self */ + public get does(): this { + return this; + } + + /** Syntactic sugar, return self */ + public get is(): this { + return this; + } + + /** Negate query, return self */ + public get not(): this { + this.negate(); + return this; + } + + protected negate(): void { + this.negated = !this.negated; + } + + protected abstract construct(): string; + + public toString(): string { + return `(${this.negated ? "-" : ""}(@${this.field}:${this.construct()}))`; + } +} \ No newline at end of file diff --git a/src/search/search-builders/boolean.ts b/src/search/search-builders/boolean.ts new file mode 100644 index 0000000..0b92f99 --- /dev/null +++ b/src/search/search-builders/boolean.ts @@ -0,0 +1,33 @@ +import { SearchField } from "./base"; + +import type { ParseSchema } from "../../typings"; +import type { Search } from "../search"; + +export class BooleanField> extends SearchField { + + public eq(value: boolean): Search { + this.value = value; + this.search._query.push(this); + return this.search; + } + + public equals(value: boolean): Search { + return this.eq(value); + } + + public equalsTo(value: boolean): Search { + return this.eq(value); + } + + public true(): Search { + return this.eq(true); + } + + public false(): Search { + return this.eq(false); + } + + protected construct(): string { + return `{${this.value}${this.or.length > 0 ? ` | ${this.or.join(" | ")}` : ""}}`; + } +} \ No newline at end of file diff --git a/src/search/search-builders/date.ts b/src/search/search-builders/date.ts new file mode 100644 index 0000000..62dfdf8 --- /dev/null +++ b/src/search/search-builders/date.ts @@ -0,0 +1,100 @@ +import { SearchField } from "./base"; + +import type { ParseSchema } from "../../typings"; +import type { Search } from "../search"; + +export class DateField> extends SearchField { + + declare protected value: [string, string]; + + public eq(value: Date | number | string): Search { + const time = this.#getTime(value); + this.value = [time, time]; + this.search._query.push(this); + return this.search; + } + + public gt(value: Date | number | string): Search { + this.value = [`(${this.#getTime(value)}`, "+inf"]; + this.search._query.push(this); + return this.search; + } + + public gte(value: Date | number | string): Search { + this.value = [this.#getTime(value), "+inf"]; + this.search._query.push(this); + return this.search; + } + + public lt(value: Date | number | string): Search { + this.value = ["-inf", `(${this.#getTime(value)}`]; + this.search._query.push(this); + return this.search; + } + + public lte(value: Date | number | string): Search { + this.value = ["-inf", this.#getTime(value)]; + this.search._query.push(this); + return this.search; + } + + public between(lower: Date | number | string, upper: Date | number | string): Search { + this.value = [this.#getTime(lower), this.#getTime(upper)]; + this.search._query.push(this); + return this.search; + } + + public equals(value: Date | number | string): Search { + return this.eq(value); + } + + public equalsTo(value: Date | number | string): Search { + return this.eq(value); + } + + public greaterThan(value: Date | number | string): Search { + return this.gt(value); + } + + public greaterThanOrEqualTo(value: Date | number | string): Search { + return this.gte(value); + } + + public lessThan(value: Date | number | string): Search { + return this.lt(value); + } + + public lessThanOrEqualTo(value: Date | number | string): Search { + return this.lte(value); + } + + public on(value: Date | number | string): Search { + return this.eq(value); + } + + public after(value: Date | number | string): Search { + return this.gt(value); + } + + public before(value: Date | number | string): Search { + return this.lt(value); + } + + public onOrAfter(value: Date | number | string): Search { + return this.gte(value); + } + + public onOrBefore(value: Date | number | string): Search { + return this.lte(value); + } + + protected construct(): string { + return `[${this.value.join(" ")}]`; + } + + #getTime(value: Date | number | string): string { + if (value instanceof Date) return value.getTime().toString(); + if (typeof value === "string") return new Date(value).getTime().toString(); + return value.toString(); + } +} \ No newline at end of file diff --git a/src/search/search-builders/index.ts b/src/search/search-builders/index.ts new file mode 100644 index 0000000..466969f --- /dev/null +++ b/src/search/search-builders/index.ts @@ -0,0 +1,8 @@ +export * from "./base"; +export * from "./string"; +export * from "./number"; +export * from "./boolean"; +export * from "./text"; +export * from "./date"; +export * from "./point"; +export * from "./vector"; \ No newline at end of file diff --git a/src/search/search-builders/number.ts b/src/search/search-builders/number.ts new file mode 100644 index 0000000..6b20297 --- /dev/null +++ b/src/search/search-builders/number.ts @@ -0,0 +1,73 @@ +import { SearchField } from "./base"; + +import type { ParseSchema } from "../../typings"; +import type { Search } from "../search"; + +export class NumberField> extends SearchField { + + declare protected value: [string, string]; + + public eq(value: number): Search { + this.value = [value.toString(), value.toString()]; + this.search._query.push(this); + return this.search; + } + + public gt(value: number): Search { + this.value = [`(${value}`, "+inf"]; + this.search._query.push(this); + return this.search; + } + + public gte(value: number): Search { + this.value = [value.toString(), "+inf"]; + this.search._query.push(this); + return this.search; + } + + public lt(value: number): Search { + this.value = ["-inf", `(${value}`]; + this.search._query.push(this); + return this.search; + } + + public lte(value: number): Search { + this.value = ["-inf", value.toString()]; + this.search._query.push(this); + return this.search; + } + + public between(lower: number, upper: number): Search { + this.value = [lower.toString(), upper.toString()]; + this.search._query.push(this); + return this.search; + } + + public equals(value: number): Search { + return this.eq(value); + } + + public equalsTo(value: number): Search { + return this.eq(value); + } + + public greaterThan(value: number): Search { + return this.gt(value); + } + + public greaterThanOrEqualTo(value: number): Search { + return this.gte(value); + } + + public lessThan(value: number): Search { + return this.lt(value); + } + + public lessThanOrEqualTo(value: number): Search { + return this.lte(value); + } + + protected construct(): string { + return `[${this.value.join(" ")}]`; + } +} \ No newline at end of file diff --git a/src/search/search-builders/point.ts b/src/search/search-builders/point.ts new file mode 100644 index 0000000..d70d03d --- /dev/null +++ b/src/search/search-builders/point.ts @@ -0,0 +1,137 @@ +import { SearchField } from "./base"; + +import type { Point, ParseSchema, Units } from "../../typings"; +import type { Search } from "../search"; + +export type CircleFunction = (circle: Circle) => Circle; + +export class Circle { + + /** @internal */ + public _longitude: number = 0; + + /** @internal */ + public _latitude: number = 0; + + /** @internal */ + public size: number = 1; + + /** @internal */ + public units: Units = "m"; + + public longitude(value: number): Circle { + this._longitude = value; + return this; + } + + public latitude(value: number): Circle { + this._latitude = value; + return this; + } + + public origin(point: Point): Circle; + public origin(longitude: number, latitude: number): Circle; + public origin(pointOrLongitude: number | Point, latitude?: number): Circle { + if (typeof pointOrLongitude === "object") { + const { longitude, latitude: lat } = pointOrLongitude; + this._longitude = longitude; + this._latitude = lat; + return this; + } + + this._longitude = pointOrLongitude; + latitude && (this._latitude = latitude); + return this; + } + + public radius(size: number): Circle { + this.size = size; + return this; + } + + public get meters(): Circle { + this.units = "m"; + return this; + } + + public get meter(): Circle { + return this.meters; + } + + public get m(): Circle { + return this.meters; + } + + public get kilometers(): Circle { + this.units = "km"; + return this; + } + + public get kilometer(): Circle { + return this.kilometers; + } + + public get km(): Circle { + return this.kilometers; + } + + public get feet(): Circle { + this.units = "ft"; + return this; + } + + public get foot(): Circle { + return this.feet; + } + + public get ft(): Circle { + return this.feet; + } + + public get miles(): Circle { + this.units = "mi"; + return this; + } + + public get mile(): Circle { + return this.miles; + } + + public get mi(): Circle { + return this.miles; + } + +} + +export class PointField> extends SearchField { + #circle: Circle = new Circle(); + + public override eq(fn: CircleFunction | Circle): Search { + this.#circle = typeof fn === "function" ? fn(this.#circle) : fn; + this.search._query.push(this); + return this.search; + } + + public override equals(fn: CircleFunction | Circle): Search { + return this.eq(fn); + } + + public override equalsTo(fn: CircleFunction | Circle): Search { + return this.eq(fn); + } + + public inRadius(fn: CircleFunction | Circle): Search { + return this.eq(fn); + } + + public inCircle(fn: CircleFunction | Circle): Search { + return this.eq(fn); + } + + protected construct(): string { + // eslint-disable-next-line @typescript-eslint/naming-convention + const { _longitude, _latitude, size, units } = this.#circle; + return `[${_longitude} ${_latitude} ${size} ${units}]`; + } + +} \ No newline at end of file diff --git a/src/search/search-builders/string.ts b/src/search/search-builders/string.ts new file mode 100644 index 0000000..4559f0a --- /dev/null +++ b/src/search/search-builders/string.ts @@ -0,0 +1,45 @@ +import { SearchField } from "./base"; + +import type { ParseSchema } from "../../typings"; +import type { Search } from "../search"; + +export class StringField> extends SearchField { + + public eq(...value: Array): Search; + public eq(value: Array): Search; + public eq(value: Array | string): Search { + return this.#handleMultipleFields(Array.isArray(value) ? value : arguments); + } + + public equals(...value: Array): Search; + public equals(value: Array): Search; + public equals(): Search { + return this.eq(...arguments); + } + + public equalsTo(...value: Array): Search; + public equalsTo(value: Array): Search; + public equalsTo(): Search { + return this.eq(...arguments); + } + + protected construct(): string { + return `{${this.value}${this.or.length > 0 ? ` | ${this.or.join(" | ")}` : ""}}`; + } + + /** @internal */ + #handleMultipleFields(value: Array | IArguments): Search { + const length = value.length; + + this.value = value[0]; + + if (length > 1) { + for (let i = 1; i < length; i++) { + this.or.push(value[i]); + } + } + + this.search._query.push(this); + return this.search; + } +} \ No newline at end of file diff --git a/src/search/search-builders/text.ts b/src/search/search-builders/text.ts new file mode 100644 index 0000000..ca701f2 --- /dev/null +++ b/src/search/search-builders/text.ts @@ -0,0 +1,91 @@ +import { SearchField } from "./base"; + +import type { ParseSchema } from "../../typings"; +import type { Search } from "../search"; + +export class TextField> extends SearchField { + + protected override value: { + val: string, + exact: boolean + } = { val: "", exact: false }; + + public eq(...value: Array): Search; + public eq(value: Array): Search; + public eq(value: Array | string): Search { + return this.#handleMultipleFields(Array.isArray(value) ? value : arguments); + } + + public equals(...value: Array): Search; + public equals(value: Array): Search; + public equals(): Search { + return this.eq(...arguments); + } + + public equalsTo(...value: Array): Search; + public equalsTo(value: Array): Search; + public equalsTo(): Search { + return this.eq(...arguments); + } + + public exact(...value: Array): Search; + public exact(value: Array): Search; + public exact(value: Array | string): Search { + return this.#handleMultipleFields(Array.isArray(value) ? value : arguments, true); + } + + public match(...value: Array): Search; + public match(value: Array): Search; + public match(): Search { + return this.eq(...arguments); + } + + public matches(...value: Array): Search; + public matches(value: Array): Search; + public matches(): Search { + return this.eq(...arguments); + } + + public matchExact(...value: Array): Search; + public matchExact(value: Array): Search; + public matchExact(): Search { + return this.exact(...arguments); + } + + public matchExactly(...value: Array): Search; + public matchExactly(value: Array): Search; + public matchExactly(): Search { + return this.exact(...arguments); + } + + public matchesExactly(...value: Array): Search; + public matchesExactly(value: Array): Search; + public matchesExactly(): Search { + return this.exact(...arguments); + } + + public get exactly(): Exclude { + this.value.exact = true; + return this; + } + + protected construct(): string { + return `(${this.value.exact ? `"${this.value.val}"` : this.value.val}${this.or.length > 0 ? ` | ${this.value.exact ? this.or.map((v) => `"${v}"`).join(" | ") : this.or.join(" | ")}` : ""})`; + } + + /** @internal */ + #handleMultipleFields(value: Array | IArguments, exact: boolean = false): Search { + const length = value.length; + + this.value = { val: value[0], exact }; + + if (length > 1) { + for (let i = 1; i < length; i++) { + this.or.push(value[i]); + } + } + + this.search._query.push(this); + return this.search; + } +} \ No newline at end of file diff --git a/src/search/search-builders/vector.ts b/src/search/search-builders/vector.ts new file mode 100644 index 0000000..c5d2132 --- /dev/null +++ b/src/search/search-builders/vector.ts @@ -0,0 +1,80 @@ +import { SearchField } from "./base"; + +import type { ParseSchema } from "../../typings"; +import type { Search } from "../search"; + +export type VectorFunction = (vector: Vector) => Vector; + +export class Vector { + + /** @internal */ + public _type!: "RANGE" | "KNN"; + + /** @internal */ + public _buffer!: string; + + /** @internal */ + public _range?: number; + + /** @internal */ + public _return?: number; + + public knn(): Vector { + this._type = "KNN"; + return this; + } + + public range(range: number): Vector { + this._type = "RANGE"; + this._range = range; + return this; + } + + public from(data: Array | Float32Array | Float64Array): Vector { + this._buffer = Buffer.from(data).toString(); + return this; + } + + public return(number: number): Vector { + this._return = number; + return this; + } +} + +export class VectorField> extends SearchField { + + /** @internal */ + public _vector: Vector = new Vector(); + + public override eq(fn: VectorFunction | Vector): Search { + this._vector = typeof fn === "function" ? fn(this._vector) : fn; + if (this._vector._type === "RANGE") this.search._query.push(this); + else this.search._vector = this; + return this.search; + } + + public override equals(fn: VectorFunction | Vector): Search { + return this.eq(fn); + } + + public override equalsTo(fn: VectorFunction | Vector): Search { + return this.eq(fn); + } + + protected override construct(): string { + // eslint-disable-next-line @typescript-eslint/naming-convention + const { _range } = this._vector; + return `[VECTOR_RANGE ${_range} $BLOB]`; + } + + public override toString(): string { + if (this._vector._type === "RANGE") { + return `(${this.negated ? "-" : ""}(@${this.field}:${this.construct()}))`; + } else { + // eslint-disable-next-line @typescript-eslint/naming-convention + const { _type, _return } = this._vector; + return `=>[${_type} ${_return} @${this.field} $BLOB]`; + } + } + +} \ No newline at end of file diff --git a/src/search/search.ts b/src/search/search.ts new file mode 100644 index 0000000..dd51767 --- /dev/null +++ b/src/search/search.ts @@ -0,0 +1,368 @@ +import { PrettyError } from "@infinite-fansub/logger"; + +import { JSONDocument, HASHDocument } from "../document"; + +import type { SearchOptions, SearchReply } from "redis"; + +import { + type SearchField, + StringField, + NumberField, + BooleanField, + TextField, + DateField, + PointField, + VectorField +} from "./search-builders"; + +import type { + FieldTypes, + NodeRedisClient, + MapSearchField, + ParseSchema, + ParseSearchSchema, + BaseField, + ParsedMap, + ReturnDocument, + SearchInformation +} from "../typings"; + +export type SearchReturn>> = Omit; +export type SearchSortReturn>> = Omit; + +export class Search, P extends ParseSearchSchema = ParseSearchSchema> { + readonly #client: NodeRedisClient; + readonly #schema: T; + readonly #parsedSchema: ParsedMap; + readonly #information: SearchInformation; + // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents + readonly #docType: typeof JSONDocument | typeof HASHDocument; + #workingType!: FieldTypes["type"]; + + /** + * LIMIT defaults to 0 10 + * SORTBY DIRECTION defaults to ASC + */ + #options: SearchOptions = {}; + + /** @internal */ + public _query: Array> = []; + + /** @internal */ + public _vector?: VectorField; + + public constructor( + client: NodeRedisClient, + schema: T, + parsedSchema: ParsedMap, + information: SearchInformation + ) { + this.#client = client; + this.#schema = schema; + this.#parsedSchema = parsedSchema; + this.#information = information; + + if (information.dataStructure === "HASH") this.#docType = HASHDocument; + else this.#docType = JSONDocument; + } + + public where(field: F): MapSearchField { + return this.#createWhere(field); + } + + public and(field: F): MapSearchField { + return this.#createWhere(field); + } + + public or(value: unknown): this { + if (this.#workingType === "string" || this.#workingType === "boolean" || this.#workingType === "text") { + this._query.at(-1)?.or.push(value); + } + + return this; + } + + // else(_value: unknown) { + + // } + + public sortBy(field: F, order: "ASC" | "DESC" = "ASC"): SearchSortReturn> { + this.#options.SORTBY = { BY: field, DIRECTION: order }; + return this; + } + + public sortAsc(field: F): SearchSortReturn> { + return this.sortBy(field); + } + + public sortAscending(field: F): SearchSortReturn> { + return this.sortBy(field); + } + + public sortDesc(field: F): SearchSortReturn> { + return this.sortBy(field, "DESC"); + } + + public sortDescending(field: F): SearchSortReturn> { + return this.sortBy(field, "DESC"); + } + + public async count(): Promise { + this.#options.LIMIT = { from: 0, size: 0 }; + return (await this.#search()).total; + } + + public async page(offset: number, count: number, autoFetch?: F): Promise>> { + const docs = []; + const { documents } = await this.#search({ LIMIT: { from: offset, size: count } }); + + for (let i = 0, len = documents.length; i < len; i++) { + const doc = documents[i]; + if (autoFetch) { + for (let j = 0, keys = Object.keys(this.#schema.references), le = keys.length; j < le; j++) { + const key = keys[j]; + const val = >doc.value[key]; + const temp = []; + + for (let k = 0, l = val.length; k < l; k++) { + temp.push(this.#get(val[k])); + } + + doc.value[key] = await Promise.all(temp); + } + } + + docs.push(new this.#docType(this.#schema, { + globalPrefix: this.#information.globalPrefix, + prefix: this.#information.prefix, + name: this.#information.modelName + }, doc.value, true, this.#information.skipDocumentValidation, autoFetch)); + } + + return docs; + } + + public async pageOfIds(offset: number, count: number, idOnly: boolean = false): Promise> { + const docs: Array = []; + const { documents } = await this.#search({ LIMIT: { from: offset, size: count } }, true); + + for (let j = 0, len = documents.length; j < len; j++) { + const doc = documents[j]; + docs.push(idOnly ? doc.value.$id?.toString() ?? "UNKNOWN" : doc.id); + } + + return docs; + } + + public async first(autoFetch?: F): Promise> { + return (await this.page(0, 1, autoFetch))[0]; + } + + public async firstId(withKey: boolean = false): Promise { + return (await this.pageOfIds(0, 1, withKey))[0]; + } + + public async min(field: F, autoFetch?: AF): Promise> { + return await this.sortBy(field, "ASC").first(autoFetch); + } + + public async minId(field: F): Promise { + return await this.sortBy(field, "ASC").firstId(); + } + + public async max(field: F, autoFetch?: AF): Promise> { + return await this.sortBy(field, "DESC").first(autoFetch); + } + + public async maxId(field: F): Promise { + return await this.sortBy(field, "DESC").firstId(); + } + + public async all(autoFetch?: F): Promise>> { + const docs = []; + const { documents } = await this.#search({ LIMIT: { from: 0, size: (await this.#search({ LIMIT: { from: 0, size: 0 } })).total } }); + + for (let i = 0, len = documents.length; i < len; i++) { + const doc = documents[i]; + + if (autoFetch) { + for (let j = 0, keys = Object.keys(this.#schema.references), le = keys.length; j < le; j++) { + const key = keys[j]; + const val = >doc.value[key]; + const temp = []; + + for (let k = 0, l = val.length; k < l; k++) { + temp.push(this.#get(val[k])); + } + + doc.value[key] = await Promise.all(temp); + } + } + docs.push(new this.#docType(this.#schema, { + globalPrefix: this.#information.globalPrefix, + prefix: this.#information.prefix, + name: this.#information.modelName + }, doc.value, true, this.#information.skipDocumentValidation, autoFetch)); + } + + return docs; + } + + public async allIds(idOnly: boolean = false): Promise> { + const docs: Array = []; + + const { documents } = await this.#search({ LIMIT: { from: 0, size: (await this.#search({ LIMIT: { from: 0, size: 0 } })).total } }); + + for (let i = 0, len = documents.length; i < len; i++) { + const doc = documents[i]; + docs.push(idOnly ? doc.value.$id?.toString() ?? "UNKNOWN" : doc.id); + } + + return docs; + } + + public async returnCount(): Promise { + return this.count(); + } + + public async returnAll(autoFetch?: F): Promise>> { + return await this.all(autoFetch); + } + + public async returnAllIds(withKey: boolean = false): Promise> { + return await this.allIds(withKey); + } + + public async returnPage(offset: number, count: number, autoFetch?: F): Promise>> { + return await this.page(offset, count, autoFetch); + } + + public async returnPageOfIds(offset: number, count: number, withKey: boolean = false): Promise> { + return await this.pageOfIds(offset, count, withKey); + } + + public async returnFirst(autoFetch?: F): Promise> { + return await this.first(autoFetch); + } + + public async returnFirstId(withKey: boolean = false): Promise { + return await this.firstId(withKey); + } + + public async returnMin(field: F, autoFetch?: AF): Promise> { + return await this.min(field, autoFetch); + } + + public async returnMinId(field: F): Promise { + return await this.minId(field); + } + + public async returnMax(field: F, autoFetch?: AF): Promise> { + return await this.max(field, autoFetch); + } + + public async returnMaxId(field: F): Promise { + return await this.maxId(field); + } + + async #search(options?: SearchOptions, keysOnly: boolean = false): Promise { + const query = this.#buildQuery(); + options = { ...this.#options, ...options }; + if (keysOnly) options.RETURN = []; + return await this.#client.ft.search(this.#information.searchIndex, query, options); + } + + async #get(id: string): Promise | null> { + const data = this.#information.dataStructure === "JSON" ? await this.#client.json.get(id) : await this.#client.hGetAll(id); + + if (data === null) return null; + + return new this.#docType(this.#schema, { + globalPrefix: this.#information.globalPrefix, + prefix: this.#information.prefix, + name: this.#information.modelName + }, data, true, this.#information.skipDocumentValidation, false); + } + + #buildQuery(): string { + let query = ""; + if (this._query.length === 0) query = "*"; + else query = this.#parseQuery(); + + if (typeof this._vector !== "undefined") { + this.#options.DIALECT = 2; + this.#options.PARAMS = { BLOB: this._vector._vector._buffer }; + query += this._vector.toString(); + } + + return query; + } + + #parseQuery(): string { + let query = ""; + for (let i = 0, len = this._query.length; i < len; i++) { + const queryPart = this._query[i]; + if (queryPart instanceof VectorField) { + this.#options.DIALECT = 2; + this.#options.PARAMS = { BLOB: queryPart._vector._buffer }; + } + //@ts-expect-error This looks like something that should be reported + query += `${queryPart.toString()} `; + } + + return query; + } + + #createWhere(field: F): MapSearchField { + if (typeof field !== "string") throw new PrettyError(`Expected a field name but instead got '${typeof field}'`); + + const parsedField = this.#parsedSchema.get(field); + if (!parsedField) throw new PrettyError(`'${field}' doesn't exist on the schema`); + + const { path, value } = parsedField; + + return this.#defineReturn(path, value.type === "array" ? value.elements : value.type); + } + + #defineReturn(field: string, type: Exclude): BaseField { + switch (type) { + case "string": { + this.#workingType = "string"; + return new StringField(this, field); + } + case "number": { + this.#workingType = "number"; + return new NumberField(this, field); + } + case "boolean": { + this.#workingType = "boolean"; + return new BooleanField(this, field); + } + case "text": { + this.#workingType = "text"; + return new TextField(this, field); + } + case "date": { + this.#workingType = "date"; + return new DateField(this, field); + } + case "point": { + this.#workingType = "point"; + return new PointField(this, field); + } + case "vector": { + this.#workingType = "vector"; + return new VectorField(this, field); + } + case "object": { throw new PrettyError("This should not be possible"); } + } + } + + public get rawQuery(): string { + return this.#buildQuery(); + } + + public get return(): SearchReturn> { + return this; + } +} \ No newline at end of file diff --git a/src/typings/client.ts b/src/typings/client.ts new file mode 100644 index 0000000..aa3b40b --- /dev/null +++ b/src/typings/client.ts @@ -0,0 +1,21 @@ +import type { createClient } from "redis"; + +import type { MethodsDefinition } from "./methods-definition"; +import type { SchemaDefinition } from "./schema-definition"; +import type { SchemaOptions } from "./schema-options"; +import type { URLObject } from "./url-object"; +import type { Module } from "./modules"; + +export type NodeRedisClient = ReturnType; + +export interface ClientOptions> { + url?: string | URLObject; + modules?: Array; + inject?: { + schema?: { + definition?: T, + methods?: M, + options?: SchemaOptions + } + }; +} \ No newline at end of file diff --git a/src/typings/doc-base.ts b/src/typings/doc-base.ts new file mode 100644 index 0000000..2c55913 --- /dev/null +++ b/src/typings/doc-base.ts @@ -0,0 +1,3 @@ +export interface DocumentShared { + toString: () => string; +} \ No newline at end of file diff --git a/src/typings/extract-generic.ts b/src/typings/extract-generic.ts new file mode 100644 index 0000000..08bbf6f --- /dev/null +++ b/src/typings/extract-generic.ts @@ -0,0 +1,5 @@ +import type { Schema } from "../schema"; + +export type ExtractSchemaDefinition = T extends Schema ? S : never; +export type ExtractSchemaMethods = T extends Schema ? M : never; +export type ExtractParsedSchemaDefinition = T extends Schema ? P : never; \ No newline at end of file diff --git a/src/typings/field-map.ts b/src/typings/field-map.ts new file mode 100644 index 0000000..090e582 --- /dev/null +++ b/src/typings/field-map.ts @@ -0,0 +1,19 @@ +import type { SchemaDefinition } from "./schema-definition"; +import type { Point } from "./point"; + +/** + * @typeParam T - The array and/or tuple type + */ +export interface FieldMap { + string: string; + number: number; + boolean: boolean; + text: string; + date: Date | number; + point: Point; + vector: Array | Float32Array | Float64Array; + array: Array; + tuple: [T]; + object: Record; + reference: Array>; +} \ No newline at end of file diff --git a/src/typings/index.ts b/src/typings/index.ts new file mode 100644 index 0000000..68bc9e9 --- /dev/null +++ b/src/typings/index.ts @@ -0,0 +1,18 @@ +export type * from "./parsed-search-schema"; +export type * from "./search-information"; +export type * from "./methods-definition"; +export type * from "./schema-definition"; +export type * from "./map-search-fields"; +export type * from "./extract-generic"; +export type * from "./schema-options"; +export type * from "./model-options"; +export type * from "./parse-schema"; +export type * from "./point-units"; +export type * from "./return-doc"; +export type * from "./map-schema"; +export type * from "./url-object"; +export type * from "./field-map"; +export type * from "./doc-base"; +export type * from "./modules"; +export type * from "./client"; +export type * from "./point"; \ No newline at end of file diff --git a/src/typings/map-schema.ts b/src/typings/map-schema.ts new file mode 100644 index 0000000..41e0095 --- /dev/null +++ b/src/typings/map-schema.ts @@ -0,0 +1,52 @@ +import type { ParseSchema } from "./parse-schema"; +import type { ReferenceArray } from "../utils"; +import type { FieldMap } from "./field-map"; + +export type MapSchema< + T extends ParseSchema, + AF extends boolean = false, + CAS extends boolean = false +> = MapSchemaData & MapSchemaReferences; + +type MapSchemaData["data"], CAS extends boolean = false> = { + [K in keyof T as T[K]["type"] extends "object" ? K : T[K]["optional"] extends true ? never : T[K]["default"] extends {} ? CAS extends true ? never : K : never]: _MapSchemaData +} & { + [K in keyof T as T[K]["type"] extends "object" ? never : T[K]["optional"] extends true ? K : T[K]["default"] extends {} ? CAS extends true ? K : never : K]?: _MapSchemaData + }; + +// eslint-disable-next-line @typescript-eslint/naming-convention +type _MapSchemaData["data"][number]> = T extends { properties: unknown } + ? T["properties"] extends ParseSchema + ? MapSchema + : T["properties"] extends ParseSchema["data"] + ? MapSchemaData + : unknown + : T extends { elements: unknown } + ? T["elements"] extends [unknown, ...Array] + ? Test + : T["elements"] extends object + ? Array> + : FieldMap["array"] + : T extends { algorithm: unknown } + ? T extends { vecType: "FLOAT32" } + ? Float32Array | Array + : T extends { vecType: "FLOAT64" } + ? Float64Array | Array + : unknown + : FieldMap[T["type"]] + ; + +type MapSchemaReferences["references"], AF extends boolean = false, CAS extends boolean = false> = CAS extends true ? { + [K in keyof T]?: Array +} : { + [K in keyof T]: _MapSchemaReferences + }; + +// eslint-disable-next-line @typescript-eslint/naming-convention +type _MapSchemaReferences["references"][number], AF extends boolean = false> = AF extends true + ? Array> + : ReferenceArray; + +type Test = { + [K in keyof T]: T[K] extends ParseSchema["data"][number] ? _MapSchemaData : never +}; \ No newline at end of file diff --git a/src/typings/map-search-fields.ts b/src/typings/map-search-fields.ts new file mode 100644 index 0000000..9381eab --- /dev/null +++ b/src/typings/map-search-fields.ts @@ -0,0 +1,58 @@ +import type { ParseSchema } from "./parse-schema"; +import type { + BooleanField, + DateField, + NumberField, + PointField, + StringField, + TextField, + VectorField +} from "../search/search-builders"; + +export type MapSearchField, T extends ParseSearchSchema> = T[K] extends "string" + ? StringField + : T[K] extends "number" + ? NumberField + : T[K] extends "boolean" + ? BooleanField + : T[K] extends "text" + ? TextField + : T[K] extends "date" + ? DateField + : T[K] extends "point" + ? PointField + : T[K] extends "vector" + ? VectorField + : never; + +export type SchemaToStrings["data"], K extends keyof T = keyof T> = K extends string + ? T[K] extends { schema: unknown } + ? never + : T[K] extends { index: false } + ? never + : T[K] extends { properties: any } + ? `${K}.${SchemaToStrings}` + : T[K] extends { elements: unknown } + ? T[K]["elements"] extends [unknown, ...Array] + ? `${K}.${SchemaToStrings<{ + [U in keyof T[K]["elements"]as U extends `${number}` ? U : never]: T[K]["elements"][U] + }>}` + : K + : K + : never; + +export type GetFinalProperty["data"]> = T extends `${infer Head}.${infer Tail}` + ? S[Head] extends { properties: any } + ? GetFinalProperty + : S[Head] extends { elements: unknown } + ? GetFinalProperty + : never + : S[T] extends { elements: unknown } + ? S[T]["elements"] extends {} + ? S[T]["elements"] + : "string" + : S[T]["type"]; + +export type ParseSearchSchema["data"]> = { + [K in SchemaToStrings]: GetFinalProperty +}; \ No newline at end of file diff --git a/src/typings/methods-definition.ts b/src/typings/methods-definition.ts new file mode 100644 index 0000000..f0d9c1f --- /dev/null +++ b/src/typings/methods-definition.ts @@ -0,0 +1,6 @@ +import type { SchemaDefinition } from "./schema-definition"; +import type { Schema } from "../schema"; +import type { Model } from "../model"; + +export type MethodsDefinition = + Record>, ...args: Array) => unknown>; \ No newline at end of file diff --git a/src/typings/model-options.ts b/src/typings/model-options.ts new file mode 100644 index 0000000..f063a8a --- /dev/null +++ b/src/typings/model-options.ts @@ -0,0 +1,9 @@ +import type { SchemaOptions } from "./schema-options"; + +export interface ModelOptions extends SchemaOptions { + globalPrefix: string; + prefix: string; + + /** Default prefix */ + version: string; +} \ No newline at end of file diff --git a/src/typings/modules.ts b/src/typings/modules.ts new file mode 100644 index 0000000..d042253 --- /dev/null +++ b/src/typings/modules.ts @@ -0,0 +1,12 @@ +import type { Client } from "../client"; + +export interface Module { + name: string; + ctor: new (client: Client) => unknown; +} + +export type ExtractName = Extract | ([T] extends [[]] ? [] : { [K in keyof T]: ExtractName }); + +export type WithModules, F = true> = F extends true ? { [N in M[number]["name"]]: CTORType } : never; + +type CTORType = T extends new () => infer U ? U : never; \ No newline at end of file diff --git a/src/typings/parse-schema.ts b/src/typings/parse-schema.ts new file mode 100644 index 0000000..8b904b2 --- /dev/null +++ b/src/typings/parse-schema.ts @@ -0,0 +1,101 @@ +import type { ExtractParsedSchemaDefinition } from "./extract-generic"; +import type { + ArrayField, + BaseField, + FlatVector, + HNSWVector, + ObjectField, + ReferenceField, + SchemaDefinition, + TupleField, + VectorField +} from "./schema-definition"; + +export type ParseSchema = { + data: { + [K in keyof T as T[K] extends ReferenceField ? never : K]: T[K] extends ObjectField + ? { + [P in keyof Required]: P extends "properties" + ? T[K][P] extends {} ? ParseSchema["data"] : undefined + : T[K][P] extends {} ? T[K][P] : Fill

+ } + : T[K] extends ArrayField + ? { + [P in keyof Required]: P extends "elements" + ? T[K][P] extends SchemaDefinition ? ParseSchema["data"] + : T[K][P] extends {} ? T[K][P] : "string" + : Fill

+ } + : T[K] extends TupleField + ? { + [P in keyof Required]: P extends "elements" + ? T extends Record> + ? { + [U in keyof V]: V[U] extends string + ? CreateDefinitionFromString + : V[U] extends SchemaDefinition + ? ParseSchema<{ $: { type: "object", properties: V[U] } }>["data"]["$"] + : never + } + : never + : T[K][P] extends {} ? T[K][P] : Fill

+ } + : T[K] extends VectorField + ? T[K] extends FlatVector + ? { + [P in keyof Required]: T[K][P] extends {} ? T[K][P] : Fill

+ } + : T[K] extends HNSWVector + ? { + [P in keyof Required]: T[K][P] extends {} ? T[K][P] : Fill

+ } + : never + : T[K] extends BaseField + ? { + [P in keyof Required]: T[K][P] extends {} ? T[K][P] : Fill

+ } + : CreateDefinitionFromString + }, + references: { + [K in keyof T as T[K] extends ReferenceField ? K : never]: T[K] extends ReferenceField + ? { + [P in keyof ReferenceField]: P extends "schema" + ? T[K][P] extends "self" + ? ParseSchema + : ExtractParsedSchemaDefinition + : T[K][P] + } + : never + } +}; + +export type CreateDefinitionFromString = T extends "vector" + ? { + [K in keyof Required]: K extends "type" + ? T + : K extends "algorithm" + ? "FLAT" + : K extends "vecType" + ? "FLOAT32" + : K extends "dim" + ? 128 + : K extends "distance" + ? "L2" + : Fill + } + : { + [K in keyof Required]: K extends "type" + ? T + : Fill + }; + +/** + * `T` is the same as `keyof FieldTypes` but i can't use `extends` here + */ +export type Fill = T extends "optional" + ? false + : T extends "sortable" + ? false + : T extends "index" + ? true + : undefined; \ No newline at end of file diff --git a/src/typings/parsed-search-schema.ts b/src/typings/parsed-search-schema.ts new file mode 100644 index 0000000..6f6641a --- /dev/null +++ b/src/typings/parsed-search-schema.ts @@ -0,0 +1,8 @@ +import type { FieldTypes, ObjectField, ReferenceField, TupleField } from "./schema-definition"; + +export interface Parsed { + value: Exclude; + path: string; +} + +export type ParsedMap = Map; \ No newline at end of file diff --git a/src/typings/point-units.ts b/src/typings/point-units.ts new file mode 100644 index 0000000..f1cd780 --- /dev/null +++ b/src/typings/point-units.ts @@ -0,0 +1 @@ +export type Units = "m" | "km" | "ft" | "mi"; \ No newline at end of file diff --git a/src/typings/point.ts b/src/typings/point.ts new file mode 100644 index 0000000..b151f51 --- /dev/null +++ b/src/typings/point.ts @@ -0,0 +1 @@ +export type Point = { longitude: number, latitude: number }; \ No newline at end of file diff --git a/src/typings/return-doc.ts b/src/typings/return-doc.ts new file mode 100644 index 0000000..9484c41 --- /dev/null +++ b/src/typings/return-doc.ts @@ -0,0 +1,10 @@ +import type { HASHDocument, JSONDocument } from "../document"; +import type { ParseSchema } from "./parse-schema"; +import type { MapSchema } from "./map-schema"; +import type { Schema } from "../schema"; + +export type Doc = JSONDocument | HASHDocument; + +export type ReturnDocument | ParseSchema, AF extends boolean = false> = T extends Schema + ? Doc & MapSchema + : Doc & MapSchema, AF>; \ No newline at end of file diff --git a/src/typings/schema-definition.ts b/src/typings/schema-definition.ts new file mode 100644 index 0000000..27e1f84 --- /dev/null +++ b/src/typings/schema-definition.ts @@ -0,0 +1,107 @@ +import type { FieldMap } from "./field-map"; +import type { Schema } from "../schema"; +import type { Point } from "./point"; + +export type SchemaDefinition = Record | FieldTypes>; + +export type FieldTypes = StringField | NumberField | BooleanField | TextField | DateField | PointField | ArrayField | TupleField | ObjectField | ReferenceField | VectorField; + +export type TupleElement = Exclude | SchemaDefinition | undefined; + +export interface BaseField { + type: keyof FieldMap; + optional?: boolean | undefined; + default?: FieldMap[keyof FieldMap] | undefined; + sortable?: boolean; + index?: boolean; +} + +// TAG +export interface StringField extends BaseField { + type: "string"; + default?: string | undefined; +} + +// NUMERIC +export interface NumberField extends BaseField { + type: "number"; + default?: number | undefined; +} + +// TAG +export interface BooleanField extends BaseField { + type: "boolean"; + default?: boolean | undefined; +} + +// TEXT +export interface TextField extends BaseField { + type: "text"; + default?: string | undefined; +} + +// NUMERIC +export interface DateField extends BaseField { + type: "date"; + default?: Date | number | string | undefined; +} + +// GEO +export interface PointField extends BaseField { + type: "point"; + default?: Point | undefined; +} + +// VECTOR +export interface BaseVector extends BaseField { + type: "vector"; + default?: Array | Float32Array | Float64Array | undefined; + algorithm: "FLAT" | "HNSW"; + vecType: "FLOAT32" | "FLOAT64"; + dim: number; + distance: "L2" | "IP" | "COSINE"; + cap?: number | undefined; +} + +export interface FlatVector extends BaseVector { + algorithm: "FLAT"; + size?: number | undefined; +} + +export interface HNSWVector extends BaseVector { + algorithm: "HNSW"; + m?: number | undefined; + construction?: number | undefined; + runtime?: number | undefined; + epsilon?: number | undefined; +} + +export type VectorField = FlatVector | HNSWVector; + +// FALLBACK +export interface ArrayField extends BaseField { + type: "array"; + elements?: Exclude | SchemaDefinition | undefined; + default?: Array | undefined; + separator?: string; +} + +//FALLBACK +export interface TupleField extends Omit { + type: "tuple"; + elements: [TupleElement, ...Array]; + default?: Array | undefined; +} + +// FALLBACK +export interface ObjectField extends Omit { + type: "object"; + properties?: SchemaDefinition | undefined; + default?: Record | undefined; +} + +// NON EXISTENT HANDLE AS ARRAY OF STRINGS WITH AUTOFETCH TRANSFORMING INTO AN OBJECT +export interface ReferenceField extends Pick { + type: "reference"; + schema: Schema | "self"; +} \ No newline at end of file diff --git a/src/typings/schema-options.ts b/src/typings/schema-options.ts new file mode 100644 index 0000000..2acc874 --- /dev/null +++ b/src/typings/schema-options.ts @@ -0,0 +1,7 @@ +export interface SchemaOptions { + dataStructure?: "HASH" | "JSON" | undefined; + skipDocumentValidation?: boolean | undefined; + noLogs?: boolean | undefined; + prefix?: string | undefined; + suffix?: string | (() => string) | undefined; +} \ No newline at end of file diff --git a/src/typings/search-information.ts b/src/typings/search-information.ts new file mode 100644 index 0000000..d00b840 --- /dev/null +++ b/src/typings/search-information.ts @@ -0,0 +1,6 @@ +import type { ModelOptions } from "./model-options"; + +export interface SearchInformation extends ModelOptions { + modelName: string; + searchIndex: string; +} \ No newline at end of file diff --git a/src/typings/url-object.ts b/src/typings/url-object.ts new file mode 100644 index 0000000..9690546 --- /dev/null +++ b/src/typings/url-object.ts @@ -0,0 +1,6 @@ +export interface URLObject { + username: string; + password: string; + entrypoint: string; + port?: string; +} \ No newline at end of file diff --git a/src/utils/index.ts b/src/utils/index.ts new file mode 100644 index 0000000..b53bd5b --- /dev/null +++ b/src/utils/index.ts @@ -0,0 +1,2 @@ +export * from "./reference-array"; +export * from "./parse"; \ No newline at end of file diff --git a/src/utils/parse.ts b/src/utils/parse.ts new file mode 100644 index 0000000..bbda2fc --- /dev/null +++ b/src/utils/parse.ts @@ -0,0 +1,39 @@ +import type { ParsedMap, ParseSchema } from "../typings"; + +export function parseSchemaToSearchIndex(schema: ParseSchema["data"], k?: string, p?: string): ParsedMap { + let objs: ParsedMap = new Map(); + + for (let i = 0, entries = Object.entries(schema), len = entries.length; i < len; i++) { + const [key, value] = entries[i]; + + if (value.type === "object") { + //@ts-expect-error Typescript is getting confused due to the union of array and object + if (typeof value.properties === "undefined") continue; + //@ts-expect-error Typescript is getting confused due to the union of array and object + const parsed = parseSchemaToSearchIndex(value.properties, k ? `${k}.${key}` : key, p ? `${k}_${key}` : key); + objs = new Map([...objs, ...parsed]); + continue; + } + + if (!value.index) continue; + + //@ts-expect-error Typescript is getting confused due to the union of array and object + if (value.type === "array" && typeof value.elements === "object") continue; + if (value.type === "tuple") { + //@ts-expect-error Typescript is getting confused due to the union of array and object + for (let j = 0, le = value.elements.length; j < le; j++) { + //@ts-expect-error Typescript is getting confused due to the union of array and object + const temp = value.elements[j]; + + const parsed = parseSchemaToSearchIndex({ [j]: temp }, k ? `${k}.${key}` : key, p ? `${k}_${key}` : key); + objs = new Map([...objs, ...parsed]); + + } + continue; + } + //@ts-expect-error ParseSchema type is confusing this (not much i can do rly) + objs.set(k ? `${k}.${key}` : key, { value: value, path: p ? `${p}_${key}` : key }); + } + + return objs; +} \ No newline at end of file diff --git a/src/utils/reference-array.ts b/src/utils/reference-array.ts new file mode 100644 index 0000000..66febef --- /dev/null +++ b/src/utils/reference-array.ts @@ -0,0 +1,15 @@ +import { HASHDocument, JSONDocument } from "../document"; + +import type { Doc } from "../typings"; + +export class ReferenceArray extends Array { + public reference(...recordOrDoc: Array): this { + for (let i = 0, len = recordOrDoc.length; i < len; i++) { + const tempVal = recordOrDoc[i]; + const tempId = tempVal instanceof JSONDocument || tempVal instanceof HASHDocument ? tempVal.$record_id : tempVal; + if (this.includes(tempId)) continue; + this.push(tempId); + } + return this; + } +} \ No newline at end of file diff --git a/src/utils/symbols.ts b/src/utils/symbols.ts new file mode 100644 index 0000000..6d2b571 --- /dev/null +++ b/src/utils/symbols.ts @@ -0,0 +1,2 @@ +export const schemaData = Symbol("schemaData"); +export const methods = Symbol("methods"); \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 92c497e..c952c95 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,24 +2,43 @@ "compilerOptions": { "target": "ESNext", "module": "CommonJS", - "rootDir": ".", - "outDir": "./dist", - "declaration": true, + "moduleResolution": "node", + "outDir": "dist", + "baseUrl": ".", "strict": true, + "allowUnreachableCode": false, + "allowUnusedLabels": false, + "exactOptionalPropertyTypes": true, + "noFallthroughCasesInSwitch": true, + "noImplicitAny": true, + "noImplicitOverride": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "strictFunctionTypes": true, + "strictNullChecks": true, + "strictPropertyInitialization": true, + "useUnknownInCatchVariables": true, + "resolveJsonModule": true, + "declaration": true, + "declarationMap": true, + "importHelpers": true, + "noEmitOnError": true, "esModuleInterop": true, "forceConsistentCasingInFileNames": true, - "types": ["vitest/globals"], - "paths": { - "$lib/*": ["./lib/*"] - }, - "noUncheckedIndexedAccess": true + "allowSyntheticDefaultImports": true, + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "incremental": false, + "stripInternal": true }, - "exclude": [ - "node_modules", + "include": [ + "src/**/*" ], "typedocOptions": { "entryPoints": [ - "lib/index.ts" + "src/index.ts" ], "out": "docs", "excludePrivate": true, diff --git a/vitest.config.js b/vitest.config.js index 461fd0a..25f5b8e 100644 --- a/vitest.config.js +++ b/vitest.config.js @@ -7,14 +7,12 @@ export default defineConfig({ clearMocks: true, isolate: false, coverage: { - provider: 'istanbul', + reporter: ["text", "html"], exclude: [ - 'spec/*' + "spec/*", + "src/typings/*" ] - }, - exclude: [ - './node_modules/**', - ] + } }, resolve: { alias: { From 613d7b91812807f2d41b2542d573872c30ba5fb2 Mon Sep 17 00:00:00 2001 From: DidaS Date: Sat, 9 Sep 2023 13:22:32 +0100 Subject: [PATCH 3/4] Merge `v0.12` --- CHANGELOG | 143 ------- examples/table-module.ts | 21 +- package.json | 13 +- pnpm-lock.yaml | 371 +++++++++--------- spec/constants.ts | 30 +- spec/schema/array.spec.ts | 30 +- spec/schema/boolean.spec.ts | 24 +- spec/schema/date.spec.ts | 24 +- spec/schema/number.spec.ts | 24 +- spec/schema/object.spec.ts | 70 +--- spec/schema/point.spec.ts | 24 +- spec/schema/string.spec.ts | 24 +- spec/schema/text.spec.ts | 24 +- src/client.ts | 47 ++- .../document-helpers/general-helpers.ts | 192 +++++---- src/document/document-helpers/hash-helpers.ts | 233 +++++------ src/document/document-helpers/json-helpers.ts | 167 ++++---- src/document/hash-document.ts | 195 ++++----- src/document/json-document.ts | 127 +++--- src/model.ts | 142 ++----- src/schema.ts | 53 ++- src/search/search-builders/base.ts | 10 +- src/search/search-builders/bigint.ts | 45 +++ src/search/search-builders/index.ts | 9 +- src/search/search-builders/number.ts | 26 +- src/search/search-builders/string.ts | 16 +- src/search/search-builders/text.ts | 2 +- src/search/search.ts | 108 ++--- src/typings/doc-base.ts | 2 +- src/typings/field-map.ts | 1 + src/typings/index.ts | 1 + src/typings/map-schema.ts | 41 +- src/typings/map-search-fields.ts | 43 +- src/typings/parse-schema.ts | 66 +++- src/typings/parsed-search-schema.ts | 13 +- src/typings/return-doc.ts | 6 +- src/typings/schema-definition.ts | 72 +++- src/typings/schema-options.ts | 28 +- src/typings/utils.ts | 15 + src/utils/index.ts | 2 +- src/utils/parse-to-search-index.ts | 159 ++++++++ src/utils/parse.ts | 39 -- src/utils/reference-array.ts | 4 +- 43 files changed, 1375 insertions(+), 1311 deletions(-) delete mode 100644 CHANGELOG create mode 100644 src/search/search-builders/bigint.ts create mode 100644 src/typings/utils.ts create mode 100644 src/utils/parse-to-search-index.ts delete mode 100644 src/utils/parse.ts diff --git a/CHANGELOG b/CHANGELOG deleted file mode 100644 index 68e7db8..0000000 --- a/CHANGELOG +++ /dev/null @@ -1,143 +0,0 @@ -# Changelog - -*Functional* changes to the public interface of Redis OM for Node.js will be listed here, by version. *Non-functional* changes may also be included, if they are particulary noteworthy or might impact developers using Redis OM. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and Redis OM adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## 0.4.0 - 2023-03-07 -### Added -- Added methods to *Repository* to allow you to provide your own *entityId* instead of relying on a generated one. -- Added support for Redis clustering by passing in a Node Redis connection created with `createCluster`. -- Added better and more meaningful exceptions. - -### Changed -- *Schema* now supports nested JSON via the `path` option. -- *Schema* now supports specifying the field for a Hash via the `field` option. -- *Schema* now supports `async` functions for *IdStrategy* allowing you to slow down your saves with I/O calls to generate IDs. -- *Repository* now allows adding Entities with properties that are not in the *Schema*. These can even be nested if you are using JSON. -- *Repository* can be now be constructed directly with `new` in addition to being created from a *Client* via `.fetchRepository`. -- *Repository* can now directly be handed an open Node Redis connection. No need to create a *Client* instance. -- *Client* deprecated in favor of using Node Redis connections. - -### Removed -- *Breaking Change* Removed *Entity* class. While still a type, it's no longer a class and is mostly just an alias for a JavaScript object. -- *Breaking Change* Removed `.execute` method on *Client*. Use Node Redis instead. -- *Breaking Change* Removed `.createEntity` and `.createAndSave` methods on *Repository*. Entities are simply JavaScript objects—to create one just create a JavaScript object and call `.save`. -- As *Entity* is no longer a class, custom methods on an *Entity* are no longer supported. - -## 0.3.6 - 2022-07-12 -### Changed -- *Breaking Change* Removed `.fetchMany` and replaced it with a variadic version of `.fetch`. -- Assorted internal cleanup. -- Changed test suite to use vitest. - -## 0.3.5 - 2022-06-21 -### Added -- Added additional field attributes to control RediSearch indexing behavior. -- Added search methods to retreive just key name or just *entityIds* instead of the entire entity. Methods include: `.returnAllKeys`, `.returnAllIds`, `.returnFirstKey`, `.returnFirstId`, `.returnPageOfKeys`, `.returnPageOfIds`, `.returnMinKey`, `.returnMinId`, `.returnMaxKey`, `.returnMaxId` -- Added `.fetchMany` to *Repository* which variadicly takes multiple *entityIds* and returns an array of fetch entities. - -### Changed -- Search methods that return a single item (like `.first`, `.min`, etc.) and can return *null* are now marked as returning *null* for TypeScript users. -- Repository `.remove` method is now variadic and takes one or more *entityIds* to remove. - -## 0.3.4 - 2022-06-07 -### Changed -- Added validation for 'point' fields. Throws error when provided invalid longitude or latitude. - -### Fixed -- Fixed missing dependency for tslib. - - -## 0.3.3 - 2022-05-05 -### Changed -- Reverted breaking change that changed field `type` from 'string[]' to 'array'. The correct field type is 'string[]'. - - -## 0.3.2 - 2022-05-03 -### Changed -- *Breaking Change*: Date values are stored in EPOCH seconds to align with [date/time APPLY functions](https://redis.io/docs/stack/search/reference/aggregations/#list-of-datetime-apply-functions). - -## 0.3.1 - 2022-05-02 -### Fixed -- Fixed error when reading `point` containing negative value from HASH. - - -## 0.3.0 - 2022-04-28 -### Changed -- Internal changes in preparation for adding embeddable objects. -- Performance improvements. -- Renamed and reorganized some types that might affect TypeScript users. -- Removed support for Node 12. -- Changed examples in README to assume top-level awaits are available. - -### Fixed -- Fixed error in sample code when calling `.use`. - - -## 0.2.1 - 2022-03-30 -### Added -- Added limited ability to sort search results using `.sortBy`. -- Added `.min` and `.max` which returns the *Entity* with the minimum or maximum value for a provided field. - -## 0.2.0 - 2022-02-25 -### Added -- Added 'point' data type that represents a spot on the globe. -- Added 'date' data type with UNIX, ISO-8601, and JavaScript flavors. -- Perform radius searches on 'point' data type using `.inRadius`. -- Perform searches on 'date' data type using `.before`, `.after`, etc. -- Issue raw RediSearch commands using `Repository.searchRaw`. -- Attach existing Node Redis connections using `.use` on *Client*. Handy if you need to do raw Redis stuff too! -- Added `.keyName` property on *Entity* that lets you to get the Redis keyname storing that *Entity*. -- Added `.expire` method to *Repository* allowing you to expire an *Entity*. - -### Changed -- String fields and searches can now be set to numbers or booleans and will be coerced to a string. -- Calls to `.createIndex` will automatically reindex if the index has changed and will do nothing if the index has not changed. -- *Breaking Change*: Changed 'array' type in Schema definitions to 'string[]' in preparation for adding other types of arrays. -- *Breaking Change*: Split 'string' type into 'string' and 'text' types, representing string values that support `.eq` searches and text values that support `.match` searches. -- *Breaking Change*: Removed the `textSearch` property from strings as it was no longer needed with 'string' and 'text' types replacing it. -- *Breaking Change*: *Schema* data structure now defaults to JSON instead of HASH. - -### Fixed -- Wildcard searches on text fields no longer fail. -- Tag searches now escape slash and backslash characters. - -### Removed -- *Breaking Change*: `Repository` constructor cannot be directly called. Use `client.fetchRepository` instead. - - -## 0.1.7 - 2022-01-07 -### Added -- Added `.isOpen` to *Client* - -### Changed -- *Client* no longer errors when opened or closed if already opened or closed. -- Update table of contents in README. - - -## 0.1.6 - 2022-01-05 -### Added -- Added `.return.first` and `.returnFirst` methods to search. Sometimes you really can eat just one. - -### Fixed -- *Entity* now has a better default JSON serialization output. - - -## 0.1.5 - 2021-12-23 -### Added -- Added ability to pass in initial values when creating an *Entity* with `.createEntity`. -- Added `.createAndSave` method to Repository that does exactly what you think it does. - -### Changed -- More fluent interface on `.return.all`, `return.page`, and `return.count` on *Search*. -- Replace `.return` with `.returnPage` in *Search* class. - - -## 0.1.4 - 2021-12-17 -### Added -- This CHANGELOG file because communicating changes matters. -- Ability to specify and override default stop word behavior when defining a *Schema*. - -### Fixed -- Doing an exact match with a stop word used to generate a cryptic error. RediSearch does not permit searching for stop words within an exact match. This error was captured, and a better error is presented that directs the user to either change their stop words or not use stop words in their query. diff --git a/examples/table-module.ts b/examples/table-module.ts index ba706c1..156eb04 100644 --- a/examples/table-module.ts +++ b/examples/table-module.ts @@ -14,17 +14,17 @@ class Table> { readonly #oldSuffix: ModelOptions["suffix"]; constructor(name: string, public model: T) { - const { suffix } = model.getOptions; + const { suffix } = model.options; this.#oldSuffix = suffix; if (typeof suffix !== "undefined") { if (typeof suffix === "function") throw new PrettyError("Using tables is not allowed with dynamic suffixes"); - model.mutateOptions = { + model.options = { suffix: `${suffix}:${name}` } } else { - model.mutateOptions = { + model.options = { suffix: name } } @@ -32,7 +32,7 @@ class Table> { /** @internal */ public _cleanUp(): void { - this.model.mutateOptions = { + this.model.options = { suffix: this.#oldSuffix } } @@ -79,17 +79,17 @@ const client = new Client({ /* Expected Redis log: - "JSON.GET" "Redis-OM:V1:test:1" + "JSON.GET" "redis-om:V1:test:1" Expected result: JSONDocument { - '$global_prefix': 'Redis-OM', + '$global_prefix': 'redis-om', '$prefix': 'V1', '$model_name': 'test', '$suffix': undefined, '$id': '1', - '$record_id': 'Redis-OM:V1:test:1', + '$record_id': 'redis-om:V1:test:1', name: 'DidaS', age: 18 } @@ -104,6 +104,7 @@ const client = new Client({ await exampleModel.withTable("table1", async (table: ExampleTable) => { await table.model.createAndSave({ $id: 1, + name: "DidaS", age: 21 }) @@ -117,17 +118,17 @@ const client = new Client({ /* Expected Redis log: - "JSON.GET" "Redis-OM:V1:test:table1:1" + "JSON.GET" "redis-om:V1:test:table1:1" Expected result: JSONDocument { - '$global_prefix': 'Redis-OM', + '$global_prefix': 'redis-om', '$prefix': 'V1', '$model_name': 'test', '$suffix': 'table1', '$id': '1', - '$record_id': 'Redis-OM:V1:test:table1:1', + '$record_id': 'redis-om:V1:test:table1:1', name: undefined, age: 21 } diff --git a/package.json b/package.json index dfb831f..5b086ef 100644 --- a/package.json +++ b/package.json @@ -45,20 +45,19 @@ "devDependencies": { "@microsoft/tsdoc": "^0.14.2", "@microsoft/tsdoc-config": "^0.16.2", - "@types/node": "^20.5.6", - "@typescript-eslint/eslint-plugin": "^6.4.1", - "@typescript-eslint/parser": "^6.4.1", + "@types/node": "^20.6.0", + "@typescript-eslint/eslint-plugin": "^6.6.0", + "@typescript-eslint/parser": "^6.6.0", "c8": "^8.0.1", - "eslint": "^8.48.0", + "eslint": "^8.49.0", "eslint-plugin-import": "^2.28.1", "eslint-plugin-tsdoc": "^0.2.17", - "redis-om": "0.4.2", "ts-node": "^10.9.1", "typescript": "^5.2.2", - "vitest": "^0.34.3" + "vitest": "^0.34.4" }, "dependencies": { - "@infinite-fansub/logger": "^2.2.1", + "@infinite-fansub/logger": "^2.2.2", "colours.js": "^3.1.2", "redis": "^4.6.8", "tslib": "^2.6.2" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7fd1673..7793080 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6,8 +6,8 @@ settings: dependencies: '@infinite-fansub/logger': - specifier: ^2.2.1 - version: 2.2.1 + specifier: ^2.2.2 + version: 2.2.2 colours.js: specifier: ^3.1.2 version: 3.1.2 @@ -26,38 +26,35 @@ devDependencies: specifier: ^0.16.2 version: 0.16.2 '@types/node': - specifier: ^20.5.6 - version: 20.5.6 + specifier: ^20.6.0 + version: 20.6.0 '@typescript-eslint/eslint-plugin': - specifier: ^6.4.1 - version: 6.4.1(@typescript-eslint/parser@6.4.1)(eslint@8.48.0)(typescript@5.2.2) + specifier: ^6.6.0 + version: 6.6.0(@typescript-eslint/parser@6.6.0)(eslint@8.49.0)(typescript@5.2.2) '@typescript-eslint/parser': - specifier: ^6.4.1 - version: 6.4.1(eslint@8.48.0)(typescript@5.2.2) + specifier: ^6.6.0 + version: 6.6.0(eslint@8.49.0)(typescript@5.2.2) c8: specifier: ^8.0.1 version: 8.0.1 eslint: - specifier: ^8.48.0 - version: 8.48.0 + specifier: ^8.49.0 + version: 8.49.0 eslint-plugin-import: specifier: ^2.28.1 - version: 2.28.1(@typescript-eslint/parser@6.4.1)(eslint@8.48.0) + version: 2.28.1(@typescript-eslint/parser@6.6.0)(eslint@8.49.0) eslint-plugin-tsdoc: specifier: ^0.2.17 version: 0.2.17 - redis-om: - specifier: 0.4.2 - version: 0.4.2 ts-node: specifier: ^10.9.1 - version: 10.9.1(@types/node@20.5.6)(typescript@5.2.2) + version: 10.9.1(@types/node@20.6.0)(typescript@5.2.2) typescript: specifier: ^5.2.2 version: 5.2.2 vitest: - specifier: ^0.34.3 - version: 0.34.3 + specifier: ^0.34.4 + version: 0.34.4 packages: @@ -275,13 +272,13 @@ packages: dev: true optional: true - /@eslint-community/eslint-utils@4.4.0(eslint@8.48.0): + /@eslint-community/eslint-utils@4.4.0(eslint@8.49.0): resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 dependencies: - eslint: 8.48.0 + eslint: 8.49.0 eslint-visitor-keys: 3.4.3 dev: true @@ -307,13 +304,13 @@ packages: - supports-color dev: true - /@eslint/js@8.48.0: - resolution: {integrity: sha512-ZSjtmelB7IJfWD2Fvb7+Z+ChTIKWq6kjda95fLcQKNS5aheVHn4IkfgRQE3sIIzTcSLwLcLZUD9UBt+V7+h+Pw==} + /@eslint/js@8.49.0: + resolution: {integrity: sha512-1S8uAY/MTJqVx0SC4epBq+N2yhuwtNwLbJYNZyhL2pO1ZVKn5HFXav5T41Ryzy9K9V7ZId2JB2oy/W4aCd9/2w==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /@humanwhocodes/config-array@0.11.10: - resolution: {integrity: sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==} + /@humanwhocodes/config-array@0.11.11: + resolution: {integrity: sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==} engines: {node: '>=10.10.0'} dependencies: '@humanwhocodes/object-schema': 1.2.1 @@ -332,8 +329,8 @@ packages: resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} dev: true - /@infinite-fansub/logger@2.2.1: - resolution: {integrity: sha512-b2alJmSUwmmjH8i6emBvF4lCjQeLg8Gsj6oWjZXbPKkLHz3edSSr7m58KGnN2//osawGFCp4He0mkZGhpTwHCA==} + /@infinite-fansub/logger@2.2.2: + resolution: {integrity: sha512-W9752P2BqStU4N4bs4DiiyajadKUoJanCGvpeTTySgKNmJNRES6QCICuaIj0Lbqt00QRp2fsC/Hg3xG8bzf9pA==} dependencies: colours.js: 3.1.2 tslib: 2.6.2 @@ -414,6 +411,7 @@ packages: '@redis/client': ^1.0.0 dependencies: '@redis/client': 1.5.9 + dev: false /@redis/client@1.5.9: resolution: {integrity: sha512-SffgN+P1zdWJWSXBvJeynvEnmnZrYmtKSRW00xl8pOPFOMJjxRR9u0frSxJpPR6Y4V+k54blJjGW7FgxbTI7bQ==} @@ -422,6 +420,7 @@ packages: cluster-key-slot: 1.1.2 generic-pool: 3.9.0 yallist: 4.0.0 + dev: false /@redis/graph@1.1.0(@redis/client@1.5.9): resolution: {integrity: sha512-16yZWngxyXPd+MJxeSr0dqh2AIOi8j9yXKcKCwVaKDbH3HTuETpDVPcLujhFYVPtYrngSco31BUcSa9TH31Gqg==} @@ -429,6 +428,7 @@ packages: '@redis/client': ^1.0.0 dependencies: '@redis/client': 1.5.9 + dev: false /@redis/json@1.0.4(@redis/client@1.5.9): resolution: {integrity: sha512-LUZE2Gdrhg0Rx7AN+cZkb1e6HjoSKaeeW8rYnt89Tly13GBI5eP4CwDVr+MY8BAYfCg4/N15OUrtLoona9uSgw==} @@ -436,6 +436,7 @@ packages: '@redis/client': ^1.0.0 dependencies: '@redis/client': 1.5.9 + dev: false /@redis/search@1.1.3(@redis/client@1.5.9): resolution: {integrity: sha512-4Dg1JjvCevdiCBTZqjhKkGoC5/BcB7k9j99kdMnaXFXg8x4eyOIVg9487CMv7/BUVkFLZCaIh8ead9mU15DNng==} @@ -443,6 +444,7 @@ packages: '@redis/client': ^1.0.0 dependencies: '@redis/client': 1.5.9 + dev: false /@redis/time-series@1.0.5(@redis/client@1.5.9): resolution: {integrity: sha512-IFjIgTusQym2B5IZJG3XKr5llka7ey84fw/NOYqESP5WUfQs9zz1ww/9+qoz4ka/S6KcGBodzlCeZ5UImKbscg==} @@ -450,6 +452,7 @@ packages: '@redis/client': ^1.0.0 dependencies: '@redis/client': 1.5.9 + dev: false /@sinclair/typebox@0.27.8: resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} @@ -474,11 +477,11 @@ packages: /@types/chai-subset@1.3.3: resolution: {integrity: sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==} dependencies: - '@types/chai': 4.3.5 + '@types/chai': 4.3.6 dev: true - /@types/chai@4.3.5: - resolution: {integrity: sha512-mEo1sAde+UCE6b2hxn332f1g1E8WfYRu6p5SvTKr2ZKC1f7gFJXk4h5PyGP9Dt6gCaG8y8XhwnXWC6Iy2cmBng==} + /@types/chai@4.3.6: + resolution: {integrity: sha512-VOVRLM1mBxIRxydiViqPcKn6MIxZytrbMpd6RJLIWKxUNr3zux8no0Oc7kJx0WAPIitgZ0gkrDS+btlqQpubpw==} dev: true /@types/istanbul-lib-coverage@2.0.4: @@ -493,16 +496,16 @@ packages: resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} dev: true - /@types/node@20.5.6: - resolution: {integrity: sha512-Gi5wRGPbbyOTX+4Y2iULQ27oUPrefaB0PxGQJnfyWN3kvEDGM3mIB5M/gQLmitZf7A9FmLeaqxD3L1CXpm3VKQ==} + /@types/node@20.6.0: + resolution: {integrity: sha512-najjVq5KN2vsH2U/xyh2opaSEz6cZMR2SetLIlxlj08nOcmPOemJmUK2o4kUzfLqfrWE0PIrNeE16XhYDd3nqg==} dev: true - /@types/semver@7.5.0: - resolution: {integrity: sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==} + /@types/semver@7.5.1: + resolution: {integrity: sha512-cJRQXpObxfNKkFAZbJl2yjWtJCqELQIdShsogr1d2MilP8dKD9TE/nEKHkJgUNHdGKCQaf9HbIynuV2csLGVLg==} dev: true - /@typescript-eslint/eslint-plugin@6.4.1(@typescript-eslint/parser@6.4.1)(eslint@8.48.0)(typescript@5.2.2): - resolution: {integrity: sha512-3F5PtBzUW0dYlq77Lcqo13fv+58KDwUib3BddilE8ajPJT+faGgxmI9Sw+I8ZS22BYwoir9ZhNXcLi+S+I2bkw==} + /@typescript-eslint/eslint-plugin@6.6.0(@typescript-eslint/parser@6.6.0)(eslint@8.49.0)(typescript@5.2.2): + resolution: {integrity: sha512-CW9YDGTQnNYMIo5lMeuiIG08p4E0cXrXTbcZ2saT/ETE7dWUrNxlijsQeU04qAAKkILiLzdQz+cGFxCJjaZUmA==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha @@ -513,25 +516,25 @@ packages: optional: true dependencies: '@eslint-community/regexpp': 4.8.0 - '@typescript-eslint/parser': 6.4.1(eslint@8.48.0)(typescript@5.2.2) - '@typescript-eslint/scope-manager': 6.4.1 - '@typescript-eslint/type-utils': 6.4.1(eslint@8.48.0)(typescript@5.2.2) - '@typescript-eslint/utils': 6.4.1(eslint@8.48.0)(typescript@5.2.2) - '@typescript-eslint/visitor-keys': 6.4.1 + '@typescript-eslint/parser': 6.6.0(eslint@8.49.0)(typescript@5.2.2) + '@typescript-eslint/scope-manager': 6.6.0 + '@typescript-eslint/type-utils': 6.6.0(eslint@8.49.0)(typescript@5.2.2) + '@typescript-eslint/utils': 6.6.0(eslint@8.49.0)(typescript@5.2.2) + '@typescript-eslint/visitor-keys': 6.6.0 debug: 4.3.4 - eslint: 8.48.0 + eslint: 8.49.0 graphemer: 1.4.0 ignore: 5.2.4 natural-compare: 1.4.0 semver: 7.5.4 - ts-api-utils: 1.0.2(typescript@5.2.2) + ts-api-utils: 1.0.3(typescript@5.2.2) typescript: 5.2.2 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/parser@6.4.1(eslint@8.48.0)(typescript@5.2.2): - resolution: {integrity: sha512-610G6KHymg9V7EqOaNBMtD1GgpAmGROsmfHJPXNLCU9bfIuLrkdOygltK784F6Crboyd5tBFayPB7Sf0McrQwg==} + /@typescript-eslint/parser@6.6.0(eslint@8.49.0)(typescript@5.2.2): + resolution: {integrity: sha512-setq5aJgUwtzGrhW177/i+DMLqBaJbdwGj2CPIVFFLE0NCliy5ujIdLHd2D1ysmlmsjdL2GWW+hR85neEfc12w==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 @@ -540,27 +543,27 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/scope-manager': 6.4.1 - '@typescript-eslint/types': 6.4.1 - '@typescript-eslint/typescript-estree': 6.4.1(typescript@5.2.2) - '@typescript-eslint/visitor-keys': 6.4.1 + '@typescript-eslint/scope-manager': 6.6.0 + '@typescript-eslint/types': 6.6.0 + '@typescript-eslint/typescript-estree': 6.6.0(typescript@5.2.2) + '@typescript-eslint/visitor-keys': 6.6.0 debug: 4.3.4 - eslint: 8.48.0 + eslint: 8.49.0 typescript: 5.2.2 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/scope-manager@6.4.1: - resolution: {integrity: sha512-p/OavqOQfm4/Hdrr7kvacOSFjwQ2rrDVJRPxt/o0TOWdFnjJptnjnZ+sYDR7fi4OimvIuKp+2LCkc+rt9fIW+A==} + /@typescript-eslint/scope-manager@6.6.0: + resolution: {integrity: sha512-pT08u5W/GT4KjPUmEtc2kSYvrH8x89cVzkA0Sy2aaOUIw6YxOIjA8ilwLr/1fLjOedX1QAuBpG9XggWqIIfERw==} engines: {node: ^16.0.0 || >=18.0.0} dependencies: - '@typescript-eslint/types': 6.4.1 - '@typescript-eslint/visitor-keys': 6.4.1 + '@typescript-eslint/types': 6.6.0 + '@typescript-eslint/visitor-keys': 6.6.0 dev: true - /@typescript-eslint/type-utils@6.4.1(eslint@8.48.0)(typescript@5.2.2): - resolution: {integrity: sha512-7ON8M8NXh73SGZ5XvIqWHjgX2f+vvaOarNliGhjrJnv1vdjG0LVIz+ToYfPirOoBi56jxAKLfsLm40+RvxVVXA==} + /@typescript-eslint/type-utils@6.6.0(eslint@8.49.0)(typescript@5.2.2): + resolution: {integrity: sha512-8m16fwAcEnQc69IpeDyokNO+D5spo0w1jepWWY2Q6y5ZKNuj5EhVQXjtVAeDDqvW6Yg7dhclbsz6rTtOvcwpHg==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 @@ -569,23 +572,23 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': 6.4.1(typescript@5.2.2) - '@typescript-eslint/utils': 6.4.1(eslint@8.48.0)(typescript@5.2.2) + '@typescript-eslint/typescript-estree': 6.6.0(typescript@5.2.2) + '@typescript-eslint/utils': 6.6.0(eslint@8.49.0)(typescript@5.2.2) debug: 4.3.4 - eslint: 8.48.0 - ts-api-utils: 1.0.2(typescript@5.2.2) + eslint: 8.49.0 + ts-api-utils: 1.0.3(typescript@5.2.2) typescript: 5.2.2 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/types@6.4.1: - resolution: {integrity: sha512-zAAopbNuYu++ijY1GV2ylCsQsi3B8QvfPHVqhGdDcbx/NK5lkqMnCGU53amAjccSpk+LfeONxwzUhDzArSfZJg==} + /@typescript-eslint/types@6.6.0: + resolution: {integrity: sha512-CB6QpJQ6BAHlJXdwUmiaXDBmTqIE2bzGTDLADgvqtHWuhfNP3rAOK7kAgRMAET5rDRr9Utt+qAzRBdu3AhR3sg==} engines: {node: ^16.0.0 || >=18.0.0} dev: true - /@typescript-eslint/typescript-estree@6.4.1(typescript@5.2.2): - resolution: {integrity: sha512-xF6Y7SatVE/OyV93h1xGgfOkHr2iXuo8ip0gbfzaKeGGuKiAnzS+HtVhSPx8Www243bwlW8IF7X0/B62SzFftg==} + /@typescript-eslint/typescript-estree@6.6.0(typescript@5.2.2): + resolution: {integrity: sha512-hMcTQ6Al8MP2E6JKBAaSxSVw5bDhdmbCEhGW/V8QXkb9oNsFkA4SBuOMYVPxD3jbtQ4R/vSODBsr76R6fP3tbA==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: typescript: '*' @@ -593,77 +596,77 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/types': 6.4.1 - '@typescript-eslint/visitor-keys': 6.4.1 + '@typescript-eslint/types': 6.6.0 + '@typescript-eslint/visitor-keys': 6.6.0 debug: 4.3.4 globby: 11.1.0 is-glob: 4.0.3 semver: 7.5.4 - ts-api-utils: 1.0.2(typescript@5.2.2) + ts-api-utils: 1.0.3(typescript@5.2.2) typescript: 5.2.2 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/utils@6.4.1(eslint@8.48.0)(typescript@5.2.2): - resolution: {integrity: sha512-F/6r2RieNeorU0zhqZNv89s9bDZSovv3bZQpUNOmmQK1L80/cV4KEu95YUJWi75u5PhboFoKUJBnZ4FQcoqhDw==} + /@typescript-eslint/utils@6.6.0(eslint@8.49.0)(typescript@5.2.2): + resolution: {integrity: sha512-mPHFoNa2bPIWWglWYdR0QfY9GN0CfvvXX1Sv6DlSTive3jlMTUy+an67//Gysc+0Me9pjitrq0LJp0nGtLgftw==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.48.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@8.49.0) '@types/json-schema': 7.0.12 - '@types/semver': 7.5.0 - '@typescript-eslint/scope-manager': 6.4.1 - '@typescript-eslint/types': 6.4.1 - '@typescript-eslint/typescript-estree': 6.4.1(typescript@5.2.2) - eslint: 8.48.0 + '@types/semver': 7.5.1 + '@typescript-eslint/scope-manager': 6.6.0 + '@typescript-eslint/types': 6.6.0 + '@typescript-eslint/typescript-estree': 6.6.0(typescript@5.2.2) + eslint: 8.49.0 semver: 7.5.4 transitivePeerDependencies: - supports-color - typescript dev: true - /@typescript-eslint/visitor-keys@6.4.1: - resolution: {integrity: sha512-y/TyRJsbZPkJIZQXrHfdnxVnxyKegnpEvnRGNam7s3TRR2ykGefEWOhaef00/UUN3IZxizS7BTO3svd3lCOJRQ==} + /@typescript-eslint/visitor-keys@6.6.0: + resolution: {integrity: sha512-L61uJT26cMOfFQ+lMZKoJNbAEckLe539VhTxiGHrWl5XSKQgA0RTBZJW2HFPy5T0ZvPVSD93QsrTKDkfNwJGyQ==} engines: {node: ^16.0.0 || >=18.0.0} dependencies: - '@typescript-eslint/types': 6.4.1 + '@typescript-eslint/types': 6.6.0 eslint-visitor-keys: 3.4.3 dev: true - /@vitest/expect@0.34.3: - resolution: {integrity: sha512-F8MTXZUYRBVsYL1uoIft1HHWhwDbSzwAU9Zgh8S6WFC3YgVb4AnFV2GXO3P5Em8FjEYaZtTnQYoNwwBrlOMXgg==} + /@vitest/expect@0.34.4: + resolution: {integrity: sha512-XlMKX8HyYUqB8dsY8Xxrc64J2Qs9pKMt2Z8vFTL4mBWXJsg4yoALHzJfDWi8h5nkO4Zua4zjqtapQ/IluVkSnA==} dependencies: - '@vitest/spy': 0.34.3 - '@vitest/utils': 0.34.3 + '@vitest/spy': 0.34.4 + '@vitest/utils': 0.34.4 chai: 4.3.8 dev: true - /@vitest/runner@0.34.3: - resolution: {integrity: sha512-lYNq7N3vR57VMKMPLVvmJoiN4bqwzZ1euTW+XXYH5kzr3W/+xQG3b41xJn9ChJ3AhYOSoweu974S1V3qDcFESA==} + /@vitest/runner@0.34.4: + resolution: {integrity: sha512-hwwdB1StERqUls8oV8YcpmTIpVeJMe4WgYuDongVzixl5hlYLT2G8afhcdADeDeqCaAmZcSgLTLtqkjPQF7x+w==} dependencies: - '@vitest/utils': 0.34.3 + '@vitest/utils': 0.34.4 p-limit: 4.0.0 pathe: 1.1.1 dev: true - /@vitest/snapshot@0.34.3: - resolution: {integrity: sha512-QyPaE15DQwbnIBp/yNJ8lbvXTZxS00kRly0kfFgAD5EYmCbYcA+1EEyRalc93M0gosL/xHeg3lKAClIXYpmUiQ==} + /@vitest/snapshot@0.34.4: + resolution: {integrity: sha512-GCsh4coc3YUSL/o+BPUo7lHQbzpdttTxL6f4q0jRx2qVGoYz/cyTRDJHbnwks6TILi6560bVWoBpYC10PuTLHw==} dependencies: magic-string: 0.30.3 pathe: 1.1.1 pretty-format: 29.6.3 dev: true - /@vitest/spy@0.34.3: - resolution: {integrity: sha512-N1V0RFQ6AI7CPgzBq9kzjRdPIgThC340DGjdKdPSE8r86aUSmeliTUgkTqLSgtEwWWsGfBQ+UetZWhK0BgJmkQ==} + /@vitest/spy@0.34.4: + resolution: {integrity: sha512-PNU+fd7DUPgA3Ya924b1qKuQkonAW6hL7YUjkON3wmBwSTIlhOSpy04SJ0NrRsEbrXgMMj6Morh04BMf8k+w0g==} dependencies: tinyspy: 2.1.1 dev: true - /@vitest/utils@0.34.3: - resolution: {integrity: sha512-kiSnzLG6m/tiT0XEl4U2H8JDBjFtwVlaE8I3QfGiMFR0QvnRDfYfdP3YvTBWM/6iJDAyaPY6yVQiCTUc7ZzTHA==} + /@vitest/utils@0.34.4: + resolution: {integrity: sha512-yR2+5CHhp/K4ySY0Qtd+CAL9f5Yh1aXrKfAT42bq6CtlGPh92jIDDDSg7ydlRow1CP+dys4TrOrbELOyNInHSg==} dependencies: diff-sequences: 29.6.3 loupe: 2.3.6 @@ -730,8 +733,8 @@ packages: is-array-buffer: 3.0.2 dev: true - /array-includes@3.1.6: - resolution: {integrity: sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==} + /array-includes@3.1.7: + resolution: {integrity: sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==} engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.2 @@ -746,8 +749,8 @@ packages: engines: {node: '>=8'} dev: true - /array.prototype.findlastindex@1.2.2: - resolution: {integrity: sha512-tb5thFFlUcp7NdNF6/MpDk/1r/4awWG1FIz3YqDf+/zJSTezBb+/5WViH41obXULHVpDzoiCLpJ/ZO9YbJMsdw==} + /array.prototype.findlastindex@1.2.3: + resolution: {integrity: sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA==} engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.2 @@ -757,8 +760,8 @@ packages: get-intrinsic: 1.2.1 dev: true - /array.prototype.flat@1.3.1: - resolution: {integrity: sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==} + /array.prototype.flat@1.3.2: + resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==} engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.2 @@ -767,8 +770,8 @@ packages: es-shim-unscopables: 1.0.0 dev: true - /array.prototype.flatmap@1.3.1: - resolution: {integrity: sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==} + /array.prototype.flatmap@1.3.2: + resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==} engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.2 @@ -777,13 +780,14 @@ packages: es-shim-unscopables: 1.0.0 dev: true - /arraybuffer.prototype.slice@1.0.1: - resolution: {integrity: sha512-09x0ZWFEjj4WD8PDbykUwo3t9arLn8NIzmmYEJFpYekOAQjpkGSyrQhNoRTcwwcFRu+ycWF78QZ63oWTqSjBcw==} + /arraybuffer.prototype.slice@1.0.2: + resolution: {integrity: sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==} engines: {node: '>= 0.4'} dependencies: array-buffer-byte-length: 1.0.0 call-bind: 1.0.2 define-properties: 1.2.0 + es-abstract: 1.22.1 get-intrinsic: 1.2.1 is-array-buffer: 3.0.2 is-shared-array-buffer: 1.0.2 @@ -889,6 +893,7 @@ packages: /cluster-key-slot@1.1.2: resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==} engines: {node: '>=0.10.0'} + dev: false /color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} @@ -1008,12 +1013,12 @@ packages: engines: {node: '>= 0.4'} dependencies: array-buffer-byte-length: 1.0.0 - arraybuffer.prototype.slice: 1.0.1 + arraybuffer.prototype.slice: 1.0.2 available-typed-arrays: 1.0.5 call-bind: 1.0.2 es-set-tostringtag: 2.0.1 es-to-primitive: 1.2.1 - function.prototype.name: 1.1.5 + function.prototype.name: 1.1.6 get-intrinsic: 1.2.1 get-symbol-description: 1.0.0 globalthis: 1.0.3 @@ -1035,11 +1040,11 @@ packages: object-keys: 1.1.1 object.assign: 4.1.4 regexp.prototype.flags: 1.5.0 - safe-array-concat: 1.0.0 + safe-array-concat: 1.0.1 safe-regex-test: 1.0.0 - string.prototype.trim: 1.2.7 - string.prototype.trimend: 1.0.6 - string.prototype.trimstart: 1.0.6 + string.prototype.trim: 1.2.8 + string.prototype.trimend: 1.0.7 + string.prototype.trimstart: 1.0.7 typed-array-buffer: 1.0.0 typed-array-byte-length: 1.0.0 typed-array-byte-offset: 1.0.0 @@ -1122,7 +1127,7 @@ packages: - supports-color dev: true - /eslint-module-utils@2.8.0(@typescript-eslint/parser@6.4.1)(eslint-import-resolver-node@0.3.9)(eslint@8.48.0): + /eslint-module-utils@2.8.0(@typescript-eslint/parser@6.6.0)(eslint-import-resolver-node@0.3.9)(eslint@8.49.0): resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==} engines: {node: '>=4'} peerDependencies: @@ -1143,15 +1148,15 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 6.4.1(eslint@8.48.0)(typescript@5.2.2) + '@typescript-eslint/parser': 6.6.0(eslint@8.49.0)(typescript@5.2.2) debug: 3.2.7 - eslint: 8.48.0 + eslint: 8.49.0 eslint-import-resolver-node: 0.3.9 transitivePeerDependencies: - supports-color dev: true - /eslint-plugin-import@2.28.1(@typescript-eslint/parser@6.4.1)(eslint@8.48.0): + /eslint-plugin-import@2.28.1(@typescript-eslint/parser@6.6.0)(eslint@8.49.0): resolution: {integrity: sha512-9I9hFlITvOV55alzoKBI+K9q74kv0iKMeY6av5+umsNwayt59fz692daGyjR+oStBQgx6nwR9rXldDev3Clw+A==} engines: {node: '>=4'} peerDependencies: @@ -1161,23 +1166,23 @@ packages: '@typescript-eslint/parser': optional: true dependencies: - '@typescript-eslint/parser': 6.4.1(eslint@8.48.0)(typescript@5.2.2) - array-includes: 3.1.6 - array.prototype.findlastindex: 1.2.2 - array.prototype.flat: 1.3.1 - array.prototype.flatmap: 1.3.1 + '@typescript-eslint/parser': 6.6.0(eslint@8.49.0)(typescript@5.2.2) + array-includes: 3.1.7 + array.prototype.findlastindex: 1.2.3 + array.prototype.flat: 1.3.2 + array.prototype.flatmap: 1.3.2 debug: 3.2.7 doctrine: 2.1.0 - eslint: 8.48.0 + eslint: 8.49.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.4.1)(eslint-import-resolver-node@0.3.9)(eslint@8.48.0) + eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.6.0)(eslint-import-resolver-node@0.3.9)(eslint@8.49.0) has: 1.0.3 is-core-module: 2.13.0 is-glob: 4.0.3 minimatch: 3.1.2 - object.fromentries: 2.0.6 - object.groupby: 1.0.0 - object.values: 1.1.6 + object.fromentries: 2.0.7 + object.groupby: 1.0.1 + object.values: 1.1.7 semver: 6.3.1 tsconfig-paths: 3.14.2 transitivePeerDependencies: @@ -1206,16 +1211,16 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /eslint@8.48.0: - resolution: {integrity: sha512-sb6DLeIuRXxeM1YljSe1KEx9/YYeZFQWcV8Rq9HfigmdDEugjLEVEa1ozDjL6YDjBpQHPJxJzze+alxi4T3OLg==} + /eslint@8.49.0: + resolution: {integrity: sha512-jw03ENfm6VJI0jA9U+8H5zfl5b+FvuU3YYvZRdZHOlU2ggJkxrlkJH4HcDrZpj6YwD8kuYqvQM8LyesoazrSOQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} hasBin: true dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.48.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@8.49.0) '@eslint-community/regexpp': 4.8.0 '@eslint/eslintrc': 2.1.2 - '@eslint/js': 8.48.0 - '@humanwhocodes/config-array': 0.11.10 + '@eslint/js': 8.49.0 + '@humanwhocodes/config-array': 0.11.11 '@humanwhocodes/module-importer': 1.0.1 '@nodelib/fs.walk': 1.2.8 ajv: 6.12.6 @@ -1379,8 +1384,8 @@ packages: resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} dev: true - /function.prototype.name@1.1.5: - resolution: {integrity: sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==} + /function.prototype.name@1.1.6: + resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.2 @@ -1396,6 +1401,7 @@ packages: /generic-pool@3.9.0: resolution: {integrity: sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==} engines: {node: '>= 4'} + dev: false /get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} @@ -1749,15 +1755,6 @@ packages: resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==} dev: true - /jsonpath-plus@7.2.0: - resolution: {integrity: sha512-zBfiUPM5nD0YZSBT/o/fbCUlCcepMIdP0CJZxM1+KgA4f2T206f6VAg9e7mX35+KlMaIc5qXW34f3BnwJ3w+RA==} - engines: {node: '>=12.0.0'} - dev: true - - /just-clone@6.2.0: - resolution: {integrity: sha512-1IynUYEc/HAwxhi3WDpIpxJbZpMCvvrrmZVqvj9EhpvbH8lls7HhdhiByjL7DkAaWlLIzpC0Xc/VPvy/UxLNjA==} - dev: true - /keyv@4.5.3: resolution: {integrity: sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==} dependencies: @@ -1842,8 +1839,8 @@ packages: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} dev: true - /mlly@1.4.1: - resolution: {integrity: sha512-SCDs78Q2o09jiZiE2WziwVBEqXQ02XkGdUy45cbJf+BpYRIjArXRJ1Wbowxkb+NaM9DWvS3UC9GiO/6eqvQ/pg==} + /mlly@1.4.2: + resolution: {integrity: sha512-i/Ykufi2t1EZ6NaPLdfnZk2AX8cs0d+mTzVKuPfqPKPatxLApaBoxJQ9x1/uckXtrS/U5oisPMDkNs0yQTaBRg==} dependencies: acorn: 8.10.0 pathe: 1.1.1 @@ -1888,8 +1885,8 @@ packages: object-keys: 1.1.1 dev: true - /object.fromentries@2.0.6: - resolution: {integrity: sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==} + /object.fromentries@2.0.7: + resolution: {integrity: sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==} engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.2 @@ -1897,8 +1894,8 @@ packages: es-abstract: 1.22.1 dev: true - /object.groupby@1.0.0: - resolution: {integrity: sha512-70MWG6NfRH9GnbZOikuhPPYzpUpof9iW2J9E4dW7FXTqPNb6rllE6u39SKwwiNh8lCwX3DDb5OgcKGiEBrTTyw==} + /object.groupby@1.0.1: + resolution: {integrity: sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ==} dependencies: call-bind: 1.0.2 define-properties: 1.2.0 @@ -1906,8 +1903,8 @@ packages: get-intrinsic: 1.2.1 dev: true - /object.values@1.1.6: - resolution: {integrity: sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==} + /object.values@1.1.7: + resolution: {integrity: sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==} engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.2 @@ -2006,12 +2003,12 @@ packages: resolution: {integrity: sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==} dependencies: jsonc-parser: 3.2.0 - mlly: 1.4.1 + mlly: 1.4.2 pathe: 1.1.1 dev: true - /postcss@8.4.28: - resolution: {integrity: sha512-Z7V5j0cq8oEKyejIKfpD8b4eBy9cwW2JWPk0+fB1HOAMsfHbnAXLLS+PfVWlzMSLQaWttKDt607I0XHmpE67Vw==} + /postcss@8.4.29: + resolution: {integrity: sha512-cbI+jaqIeu/VGqXEarWkRCCffhjgXc0qjBtXpqJhTBohMUjUQnbBr0xqX3vEKudc4iviTewcJo5ajcec5+wdJw==} engines: {node: ^10 || ^12 || >=14} dependencies: nanoid: 3.3.6 @@ -2046,16 +2043,6 @@ packages: resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} dev: true - /redis-om@0.4.2: - resolution: {integrity: sha512-sBah+ljGQY4Zm1f9/+l7HjtIuTQH/rzIX3JSLizymbr6VlTZ2ibt+U3xYKNjIA0tv/D9toEMF7HFXHMtT+l+1A==} - engines: {node: '>= 14'} - dependencies: - jsonpath-plus: 7.2.0 - just-clone: 6.2.0 - redis: 4.6.8 - ulid: 2.3.0 - dev: true - /redis@4.6.8: resolution: {integrity: sha512-S7qNkPUYrsofQ0ztWlTHSaK0Qqfl1y+WMIxrzeAGNG+9iUZB4HGeBgkHxE6uJJ6iXrkvLd1RVJ2nvu6H1sAzfQ==} dependencies: @@ -2065,6 +2052,7 @@ packages: '@redis/json': 1.0.4(@redis/client@1.5.9) '@redis/search': 1.1.3(@redis/client@1.5.9) '@redis/time-series': 1.0.5(@redis/client@1.5.9) + dev: false /regexp.prototype.flags@1.5.0: resolution: {integrity: sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==} @@ -2113,8 +2101,8 @@ packages: glob: 7.2.3 dev: true - /rollup@3.28.1: - resolution: {integrity: sha512-R9OMQmIHJm9znrU3m3cpE8uhN0fGdXiawME7aZIpQqvpS/85+Vt1Hq1/yVIcYfOmaQiHjvXkQAoJukvLpau6Yw==} + /rollup@3.29.0: + resolution: {integrity: sha512-nszM8DINnx1vSS+TpbWKMkxem0CDWk3cSit/WWCBVs9/JZ1I/XLwOsiUglYuYReaeWWSsW9kge5zE5NZtf/a4w==} engines: {node: '>=14.18.0', npm: '>=8.0.0'} hasBin: true optionalDependencies: @@ -2127,8 +2115,8 @@ packages: queue-microtask: 1.2.3 dev: true - /safe-array-concat@1.0.0: - resolution: {integrity: sha512-9dVEFruWIsnie89yym+xWTAYASdpw3CJV7Li/6zBewGf9z2i1j31rP6jnY0pHEO4QZh6N0K11bFjWmdR8UGdPQ==} + /safe-array-concat@1.0.1: + resolution: {integrity: sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==} engines: {node: '>=0.4'} dependencies: call-bind: 1.0.2 @@ -2213,8 +2201,8 @@ packages: strip-ansi: 6.0.1 dev: true - /string.prototype.trim@1.2.7: - resolution: {integrity: sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==} + /string.prototype.trim@1.2.8: + resolution: {integrity: sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==} engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.2 @@ -2222,16 +2210,16 @@ packages: es-abstract: 1.22.1 dev: true - /string.prototype.trimend@1.0.6: - resolution: {integrity: sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==} + /string.prototype.trimend@1.0.7: + resolution: {integrity: sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==} dependencies: call-bind: 1.0.2 define-properties: 1.2.0 es-abstract: 1.22.1 dev: true - /string.prototype.trimstart@1.0.6: - resolution: {integrity: sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==} + /string.prototype.trimstart@1.0.7: + resolution: {integrity: sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==} dependencies: call-bind: 1.0.2 define-properties: 1.2.0 @@ -2307,8 +2295,8 @@ packages: is-number: 7.0.0 dev: true - /ts-api-utils@1.0.2(typescript@5.2.2): - resolution: {integrity: sha512-Cbu4nIqnEdd+THNEsBdkolnOXhg0I8XteoHaEKgvsxpsbWda4IsUut2c187HxywQCvveojow0Dgw/amxtSKVkQ==} + /ts-api-utils@1.0.3(typescript@5.2.2): + resolution: {integrity: sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==} engines: {node: '>=16.13.0'} peerDependencies: typescript: '>=4.2.0' @@ -2316,7 +2304,7 @@ packages: typescript: 5.2.2 dev: true - /ts-node@10.9.1(@types/node@20.5.6)(typescript@5.2.2): + /ts-node@10.9.1(@types/node@20.6.0)(typescript@5.2.2): resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} hasBin: true peerDependencies: @@ -2335,7 +2323,7 @@ packages: '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 20.5.6 + '@types/node': 20.6.0 acorn: 8.10.0 acorn-walk: 8.2.0 arg: 4.1.3 @@ -2425,11 +2413,6 @@ packages: resolution: {integrity: sha512-bRn3CsoojyNStCZe0BG0Mt4Nr/4KF+rhFlnNXybgqt5pXHNFRlqinSoQaTrGyzE4X8aHplSb+TorH+COin9Yxw==} dev: true - /ulid@2.3.0: - resolution: {integrity: sha512-keqHubrlpvT6G2wH0OEfSW4mquYRcbe/J8NMmveoQOjUqmo+hXtO+ORCpWhdbZ7k72UtY61BL7haGxW6enBnjw==} - hasBin: true - dev: true - /unbox-primitive@1.0.2: resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} dependencies: @@ -2458,17 +2441,17 @@ packages: convert-source-map: 1.9.0 dev: true - /vite-node@0.34.3(@types/node@20.5.6): - resolution: {integrity: sha512-+0TzJf1g0tYXj6tR2vEyiA42OPq68QkRZCu/ERSo2PtsDJfBpDyEfuKbRvLmZqi/CgC7SCBtyC+WjTGNMRIaig==} + /vite-node@0.34.4(@types/node@20.6.0): + resolution: {integrity: sha512-ho8HtiLc+nsmbwZMw8SlghESEE3KxJNp04F/jPUCLVvaURwt0d+r9LxEqCX5hvrrOQ0GSyxbYr5ZfRYhQ0yVKQ==} engines: {node: '>=v14.18.0'} hasBin: true dependencies: cac: 6.7.14 debug: 4.3.4 - mlly: 1.4.1 + mlly: 1.4.2 pathe: 1.1.1 picocolors: 1.0.0 - vite: 4.4.9(@types/node@20.5.6) + vite: 4.4.9(@types/node@20.6.0) transitivePeerDependencies: - '@types/node' - less @@ -2480,7 +2463,7 @@ packages: - terser dev: true - /vite@4.4.9(@types/node@20.5.6): + /vite@4.4.9(@types/node@20.6.0): resolution: {integrity: sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==} engines: {node: ^14.18.0 || >=16.0.0} hasBin: true @@ -2508,16 +2491,16 @@ packages: terser: optional: true dependencies: - '@types/node': 20.5.6 + '@types/node': 20.6.0 esbuild: 0.18.20 - postcss: 8.4.28 - rollup: 3.28.1 + postcss: 8.4.29 + rollup: 3.29.0 optionalDependencies: fsevents: 2.3.3 dev: true - /vitest@0.34.3: - resolution: {integrity: sha512-7+VA5Iw4S3USYk+qwPxHl8plCMhA5rtfwMjgoQXMT7rO5ldWcdsdo3U1QD289JgglGK4WeOzgoLTsGFu6VISyQ==} + /vitest@0.34.4: + resolution: {integrity: sha512-SE/laOsB6995QlbSE6BtkpXDeVNLJc1u2LHRG/OpnN4RsRzM3GQm4nm3PQCK5OBtrsUqnhzLdnT7se3aeNGdlw==} engines: {node: '>=v14.18.0'} hasBin: true peerDependencies: @@ -2547,14 +2530,14 @@ packages: webdriverio: optional: true dependencies: - '@types/chai': 4.3.5 + '@types/chai': 4.3.6 '@types/chai-subset': 1.3.3 - '@types/node': 20.5.6 - '@vitest/expect': 0.34.3 - '@vitest/runner': 0.34.3 - '@vitest/snapshot': 0.34.3 - '@vitest/spy': 0.34.3 - '@vitest/utils': 0.34.3 + '@types/node': 20.6.0 + '@vitest/expect': 0.34.4 + '@vitest/runner': 0.34.4 + '@vitest/snapshot': 0.34.4 + '@vitest/spy': 0.34.4 + '@vitest/utils': 0.34.4 acorn: 8.10.0 acorn-walk: 8.2.0 cac: 6.7.14 @@ -2568,8 +2551,8 @@ packages: strip-literal: 1.3.0 tinybench: 2.5.0 tinypool: 0.7.0 - vite: 4.4.9(@types/node@20.5.6) - vite-node: 0.34.3(@types/node@20.5.6) + vite: 4.4.9(@types/node@20.6.0) + vite-node: 0.34.4(@types/node@20.6.0) why-is-node-running: 2.2.2 transitivePeerDependencies: - less diff --git a/spec/constants.ts b/spec/constants.ts index fea32ea..605d66b 100644 --- a/spec/constants.ts +++ b/spec/constants.ts @@ -4,48 +4,48 @@ export const stringSchema = new Schema({ stringField1: "string", stringField2: { type: "string" }, stringField3: { type: "string", default: "S" }, - stringField4: { type: "string", required: true }, - stringField5: { type: "string", default: "SS", required: true } + stringField4: { type: "string", optional: true }, + stringField5: { type: "string", default: "SS", optional: true } }) export const numberSchema = new Schema({ numberField1: "number", numberField2: { type: "number" }, numberField3: { type: "number", default: 3 }, - numberField4: { type: "number", required: true }, - numberField5: { type: "number", default: 5, required: true } + numberField4: { type: "number", optional: true }, + numberField5: { type: "number", default: 5, optional: true } }) export const booleanSchema = new Schema({ booleanField1: "boolean", booleanField2: { type: "boolean" }, booleanField3: { type: "boolean", default: true }, - booleanField4: { type: "boolean", required: true }, - booleanField5: { type: "boolean", default: false, required: true } + booleanField4: { type: "boolean", optional: true }, + booleanField5: { type: "boolean", default: false, optional: true } }) export const textSchema = new Schema({ textField1: "text", textField2: { type: "text" }, textField3: { type: "text", default: "T" }, - textField4: { type: "text", required: true }, - textField5: { type: "text", default: "TT", required: true } + textField4: { type: "text", optional: true }, + textField5: { type: "text", default: "TT", optional: true } }) export const dateSchema = new Schema({ dateField1: "date", dateField2: { type: "date" }, dateField3: { type: "date", default: 874195200000 }, - dateField4: { type: "date", required: true }, - dateField5: { type: "date", default: new Date(874195200000), required: true } + dateField4: { type: "date", optional: true }, + dateField5: { type: "date", default: new Date(874195200000), optional: true } }) export const pointSchema = new Schema({ pointField1: "point", pointField2: { type: "point" }, pointField3: { type: "point", default: { longitude: 3, latitude: 3 } }, - pointField4: { type: "point", required: true }, - pointField5: { type: "point", default: { longitude: 5, latitude: 5 }, required: true } + pointField4: { type: "point", optional: true }, + pointField5: { type: "point", default: { longitude: 5, latitude: 5 }, optional: true } }) export const arraySchema = new Schema({ @@ -53,8 +53,8 @@ export const arraySchema = new Schema({ arrayField2: { type: "array" }, arrayField3: { type: "array", elements: "number" }, arrayField4: { type: "array", default: ["s"] }, - arrayField5: { type: "array", required: true }, - arrayField6: { type: "array", elements: "boolean", default: [false], required: true }, + arrayField5: { type: "array", optional: true }, + arrayField6: { type: "array", elements: "boolean", default: [false], optional: true }, arrayField7: { type: "array", elements: { element1: "string" } } }) @@ -92,6 +92,6 @@ export const objectSchema = new Schema({ deep1: "S" } }, - required: true + optional: true } }) \ No newline at end of file diff --git a/spec/schema/array.spec.ts b/spec/schema/array.spec.ts index 8b76c8b..6eb8d33 100644 --- a/spec/schema/array.spec.ts +++ b/spec/schema/array.spec.ts @@ -1,28 +1,16 @@ -import { schemaData } from "../../src/utils"; +import { schemaData } from "../../src/utils/symbols"; import { arraySchema } from "../constants"; describe("array fields", () => { - test("raw data", () => { - expect(arraySchema.rawData).toStrictEqual({ - arrayField1: "array", - arrayField2: { type: "array" }, - arrayField3: { type: "array", elements: "number" }, - arrayField4: { type: "array", default: ["s"] }, - arrayField5: { type: "array", required: true }, - arrayField6: { type: "array", elements: "boolean", default: [false], required: true }, - arrayField7: { type: "array", elements: { element1: "string" } } - }); - }); - test("formatted data", () => { - expect(arraySchema[schemaData]).toStrictEqual({ - arrayField1: { type: "array", elements: "string", default: undefined, required: false }, - arrayField2: { type: "array", elements: "string", default: undefined, required: false }, - arrayField3: { type: "array", elements: "number", default: undefined, required: false }, - arrayField4: { type: "array", elements: "string", default: ["s"], required: false }, - arrayField5: { type: "array", elements: "string", default: undefined, required: true }, - arrayField6: { type: "array", elements: "boolean", default: [false], required: true }, - arrayField7: { type: "array", elements: { element1: { type: "string", default: undefined, required: false } }, default: undefined, required: false } + expect(arraySchema[schemaData].data).toStrictEqual({ + arrayField1: { type: "array", elements: "string", default: undefined, optional: false, sortable: false, index: true, separator: "," }, + arrayField2: { type: "array", elements: "string", default: undefined, optional: false, sortable: false, index: true, separator: "," }, + arrayField3: { type: "array", elements: "number", default: undefined, optional: false, sortable: false, index: true, separator: "," }, + arrayField4: { type: "array", elements: "string", default: ["s"], optional: false, sortable: false, index: true, separator: "," }, + arrayField5: { type: "array", elements: "string", default: undefined, optional: true, sortable: false, index: true, separator: "," }, + arrayField6: { type: "array", elements: "boolean", default: [false], optional: true, sortable: false, index: true, separator: "," }, + arrayField7: { type: "array", elements: { element1: { type: "string", default: undefined, optional: false, sortable: false, index: true, literal: undefined } }, default: undefined, optional: false, sortable: false, index: true, separator: "," } }); }); }) \ No newline at end of file diff --git a/spec/schema/boolean.spec.ts b/spec/schema/boolean.spec.ts index de7971c..72a8651 100644 --- a/spec/schema/boolean.spec.ts +++ b/spec/schema/boolean.spec.ts @@ -1,24 +1,14 @@ -import { schemaData } from "../../src/utils"; +import { schemaData } from "../../src/utils/symbols"; import { booleanSchema } from "../constants"; describe("boolean fields", () => { - test("raw data", () => { - expect(booleanSchema.rawData).toStrictEqual({ - booleanField1: "boolean", - booleanField2: { type: "boolean" }, - booleanField3: { type: "boolean", default: true }, - booleanField4: { type: "boolean", required: true }, - booleanField5: { type: "boolean", default: false, required: true } - }); - }); - test("formatted data", () => { - expect(booleanSchema[schemaData]).toStrictEqual({ - booleanField1: { type: "boolean", default: undefined, required: false }, - booleanField2: { type: "boolean", default: undefined, required: false }, - booleanField3: { type: "boolean", default: true, required: false }, - booleanField4: { type: "boolean", default: undefined, required: true }, - booleanField5: { type: "boolean", default: false, required: true } + expect(booleanSchema[schemaData].data).toStrictEqual({ + booleanField1: { type: "boolean", default: undefined, optional: false, sortable: false, index: true }, + booleanField2: { type: "boolean", default: undefined, optional: false, sortable: false, index: true }, + booleanField3: { type: "boolean", default: true, optional: false, sortable: false, index: true }, + booleanField4: { type: "boolean", default: undefined, optional: true, sortable: false, index: true }, + booleanField5: { type: "boolean", default: false, optional: true, sortable: false, index: true } }); }); }) \ No newline at end of file diff --git a/spec/schema/date.spec.ts b/spec/schema/date.spec.ts index 740476b..ca204c1 100644 --- a/spec/schema/date.spec.ts +++ b/spec/schema/date.spec.ts @@ -1,24 +1,14 @@ -import { schemaData } from "../../src/utils"; +import { schemaData } from "../../src/utils/symbols"; import { dateSchema } from "../constants"; describe("date fields", () => { - test("raw data", () => { - expect(dateSchema.rawData).toStrictEqual({ - dateField1: "date", - dateField2: { type: "date" }, - dateField3: { type: "date", default: 874195200000 }, - dateField4: { type: "date", required: true }, - dateField5: { type: "date", default: new Date(874195200000), required: true } - }); - }); - test("formatted data", () => { - expect(dateSchema[schemaData]).toStrictEqual({ - dateField1: { type: "date", default: undefined, required: false }, - dateField2: { type: "date", default: undefined, required: false }, - dateField3: { type: "date", default: 874195200000, required: false }, - dateField4: { type: "date", default: undefined, required: true }, - dateField5: { type: "date", default: 874195200000, required: true } + expect(dateSchema[schemaData].data).toStrictEqual({ + dateField1: { type: "date", default: undefined, optional: false, sortable: false, index: true }, + dateField2: { type: "date", default: undefined, optional: false, sortable: false, index: true }, + dateField3: { type: "date", default: 874195200000, optional: false, sortable: false, index: true }, + dateField4: { type: "date", default: undefined, optional: true, sortable: false, index: true }, + dateField5: { type: "date", default: 874195200000, optional: true, sortable: false, index: true } }); }); }) \ No newline at end of file diff --git a/spec/schema/number.spec.ts b/spec/schema/number.spec.ts index 45edf28..fd526c4 100644 --- a/spec/schema/number.spec.ts +++ b/spec/schema/number.spec.ts @@ -1,24 +1,14 @@ -import { schemaData } from "../../src/utils"; +import { schemaData } from "../../src/utils/symbols"; import { numberSchema } from "../constants"; describe("number fields", () => { - test("raw data", () => { - expect(numberSchema.rawData).toStrictEqual({ - numberField1: "number", - numberField2: { type: "number" }, - numberField3: { type: "number", default: 3 }, - numberField4: { type: "number", required: true }, - numberField5: { type: "number", default: 5, required: true } - }); - }); - test("formatted data", () => { - expect(numberSchema[schemaData]).toStrictEqual({ - numberField1: { type: "number", default: undefined, required: false }, - numberField2: { type: "number", default: undefined, required: false }, - numberField3: { type: "number", default: 3, required: false }, - numberField4: { type: "number", default: undefined, required: true }, - numberField5: { type: "number", default: 5, required: true } + expect(numberSchema[schemaData].data).toStrictEqual({ + numberField1: { type: "number", default: undefined, optional: false, sortable: false, index: true, literal: undefined }, + numberField2: { type: "number", default: undefined, optional: false, sortable: false, index: true, literal: undefined }, + numberField3: { type: "number", default: 3, optional: false, sortable: false, index: true, literal: undefined }, + numberField4: { type: "number", default: undefined, optional: true, sortable: false, index: true, literal: undefined }, + numberField5: { type: "number", default: 5, optional: true, sortable: false, index: true, literal: undefined } }); }); }) \ No newline at end of file diff --git a/spec/schema/object.spec.ts b/spec/schema/object.spec.ts index 8d8a7d5..1b144b9 100644 --- a/spec/schema/object.spec.ts +++ b/spec/schema/object.spec.ts @@ -1,81 +1,41 @@ -import { schemaData } from "../../src/utils"; +import { schemaData } from "../../src/utils/symbols"; import { objectSchema } from "../constants"; describe("object field", () => { - test("raw data", () => { - expect(objectSchema.rawData).toStrictEqual({ - objectField1: { - type: "object", - properties: { - nestedProperty1: "string", - nestedProperty2: "number", - nestedProperty3: "boolean", - nestedProperty4: "text", - nestedProperty5: "date", - nestedProperty6: "point", - nestedProperty7: "array", - nestedProperty9: { - type: "object", - properties: { - deep1: "string", - deep2: { - type: "object", - properties: { - nest: { - type: "object", - properties: { - finalNestBczImLazy: "array" - } - } - } - } - } - } - }, - default: { - nestedProperty9: { - deep1: "S" - } - }, - required: true - } - }); - }); - test("formatted data", () => { - expect(objectSchema[schemaData]).toStrictEqual({ + expect(objectSchema[schemaData].data).toStrictEqual({ objectField1: { type: "object", properties: { - nestedProperty1: { type: "string", default: undefined, required: false }, - nestedProperty2: { type: "number", default: undefined, required: false }, - nestedProperty3: { type: "boolean", default: undefined, required: false }, - nestedProperty4: { type: "text", default: undefined, required: false }, - nestedProperty5: { type: "date", default: undefined, required: false }, - nestedProperty6: { type: "point", default: undefined, required: false }, - nestedProperty7: { type: "array", elements: "string", default: undefined, required: false }, + nestedProperty1: { type: "string", default: undefined, optional: false, sortable: false, index: true, literal: undefined }, + nestedProperty2: { type: "number", default: undefined, optional: false, sortable: false, index: true, literal: undefined }, + nestedProperty3: { type: "boolean", default: undefined, optional: false, sortable: false, index: true }, + nestedProperty4: { type: "text", default: undefined, optional: false, sortable: false, index: true }, + nestedProperty5: { type: "date", default: undefined, optional: false, sortable: false, index: true }, + nestedProperty6: { type: "point", default: undefined, optional: false, sortable: false, index: true }, + nestedProperty7: { type: "array", elements: "string", default: undefined, optional: false, sortable: false, index: true, separator: "," }, nestedProperty9: { type: "object", properties: { - deep1: { type: "string", default: undefined, required: false }, + deep1: { type: "string", default: undefined, optional: false, sortable: false, index: true, literal: undefined }, deep2: { type: "object", properties: { nest: { type: "object", properties: { - finalNestBczImLazy: { type: "array", elements: "string", default: undefined, required: false } + finalNestBczImLazy: { type: "array", elements: "string", default: undefined, optional: false, sortable: false, index: true, separator: "," } }, default: undefined, - required: false + optional: false } }, default: undefined, - required: false + optional: false } }, default: undefined, - required: false + optional: false } }, default: { @@ -83,7 +43,7 @@ describe("object field", () => { deep1: "S" } }, - required: true + optional: true } }) }); diff --git a/spec/schema/point.spec.ts b/spec/schema/point.spec.ts index 13326b5..6e8be2d 100644 --- a/spec/schema/point.spec.ts +++ b/spec/schema/point.spec.ts @@ -1,24 +1,14 @@ -import { schemaData } from "../../src/utils"; +import { schemaData } from "../../src/utils/symbols"; import { pointSchema } from "../constants"; describe("point fields", () => { - test("raw data", () => { - expect(pointSchema.rawData).toStrictEqual({ - pointField1: "point", - pointField2: { type: "point" }, - pointField3: { type: "point", default: { longitude: 3, latitude: 3 } }, - pointField4: { type: "point", required: true }, - pointField5: { type: "point", default: { longitude: 5, latitude: 5 }, required: true } - }); - }); - test("formatted data", () => { - expect(pointSchema[schemaData]).toStrictEqual({ - pointField1: { type: "point", default: undefined, required: false }, - pointField2: { type: "point", default: undefined, required: false }, - pointField3: { type: "point", default: { longitude: 3, latitude: 3 }, required: false }, - pointField4: { type: "point", default: undefined, required: true }, - pointField5: { type: "point", default: { longitude: 5, latitude: 5 }, required: true } + expect(pointSchema[schemaData].data).toStrictEqual({ + pointField1: { type: "point", default: undefined, optional: false, sortable: false, index: true }, + pointField2: { type: "point", default: undefined, optional: false, sortable: false, index: true }, + pointField3: { type: "point", default: { longitude: 3, latitude: 3 }, optional: false, sortable: false, index: true }, + pointField4: { type: "point", default: undefined, optional: true, sortable: false, index: true }, + pointField5: { type: "point", default: { longitude: 5, latitude: 5 }, optional: true, sortable: false, index: true } }); }); }) \ No newline at end of file diff --git a/spec/schema/string.spec.ts b/spec/schema/string.spec.ts index 23493b9..921c06b 100644 --- a/spec/schema/string.spec.ts +++ b/spec/schema/string.spec.ts @@ -1,24 +1,14 @@ -import { schemaData } from "../../src/utils"; +import { schemaData } from "../../src/utils/symbols"; import { stringSchema } from "../constants"; describe("string fields", () => { - test("raw data", () => { - expect(stringSchema.rawData).toStrictEqual({ - stringField1: "string", - stringField2: { type: "string" }, - stringField3: { type: "string", default: "S" }, - stringField4: { type: "string", required: true }, - stringField5: { type: "string", default: "SS", required: true } - }); - }); - test("formatted data", () => { - expect(stringSchema[schemaData]).toStrictEqual({ - stringField1: { type: "string", default: undefined, required: false }, - stringField2: { type: "string", default: undefined, required: false }, - stringField3: { type: "string", default: "S", required: false }, - stringField4: { type: "string", default: undefined, required: true }, - stringField5: { type: "string", default: "SS", required: true } + expect(stringSchema[schemaData].data).toStrictEqual({ + stringField1: { type: "string", default: undefined, optional: false, sortable: false, index: true, literal: undefined }, + stringField2: { type: "string", default: undefined, optional: false, sortable: false, index: true, literal: undefined }, + stringField3: { type: "string", default: "S", optional: false, sortable: false, index: true, literal: undefined }, + stringField4: { type: "string", default: undefined, optional: true, sortable: false, index: true, literal: undefined }, + stringField5: { type: "string", default: "SS", optional: true, sortable: false, index: true, literal: undefined } }); }); }) \ No newline at end of file diff --git a/spec/schema/text.spec.ts b/spec/schema/text.spec.ts index fa91ec5..0d6e491 100644 --- a/spec/schema/text.spec.ts +++ b/spec/schema/text.spec.ts @@ -1,24 +1,14 @@ -import { schemaData } from "../../src/utils"; +import { schemaData } from "../../src/utils/symbols"; import { textSchema } from "../constants"; describe("text fields", () => { - test("raw data", () => { - expect(textSchema.rawData).toStrictEqual({ - textField1: "text", - textField2: { type: "text" }, - textField3: { type: "text", default: "T" }, - textField4: { type: "text", required: true }, - textField5: { type: "text", default: "TT", required: true } - }); - }); - test("formatted data", () => { - expect(textSchema[schemaData]).toStrictEqual({ - textField1: { type: "text", default: undefined, required: false }, - textField2: { type: "text", default: undefined, required: false }, - textField3: { type: "text", default: "T", required: false }, - textField4: { type: "text", default: undefined, required: true }, - textField5: { type: "text", default: "TT", required: true } + expect(textSchema[schemaData].data).toStrictEqual({ + textField1: { type: "text", default: undefined, optional: false, sortable: false, index: true }, + textField2: { type: "text", default: undefined, optional: false, sortable: false, index: true }, + textField3: { type: "text", default: "T", optional: false, sortable: false, index: true }, + textField4: { type: "text", default: undefined, optional: true, sortable: false, index: true }, + textField5: { type: "text", default: "TT", optional: true, sortable: false, index: true } }); }); }) \ No newline at end of file diff --git a/src/client.ts b/src/client.ts index c4e2ce3..0cb612c 100644 --- a/src/client.ts +++ b/src/client.ts @@ -8,37 +8,36 @@ import type { ExtractSchemaMethods, MethodsDefinition, SchemaDefinition, - SchemaOptions, - WithModules, NodeRedisClient, + SchemaOptions, + ClientOptions, ExtractName, + WithModules, URLObject, - Module, - ClientOptions + Narrow, + Module } from "./typings"; export class Client = {}> { #client!: NodeRedisClient; #models: Map> = new Map(); #open: boolean = false; - #prefix: string = "Redis-OM"; - - /** Please only access this from within a module or if you know what you are doing */ - public _options: ClientOptions; + #prefix: string = "redis-om"; + #options: ClientOptions; public constructor(options?: ClientOptions) { - this._options = options ?? >{}; + this.#options = options ?? >{}; - if (this._options.modules) { - for (let i = 0, len = this._options.modules.length; i < len; i++) { - const module = this._options.modules[i]; + if (this.#options.modules) { + for (let i = 0, len = this.#options.modules.length; i < len; i++) { + const module = this.#options.modules[i]; //@ts-expect-error shenanigans this[module.name] = new module.ctor(this); } } } - public async connect(url: string | URLObject = this._options.url ?? "redis://localhost:6379"): Promise { + public async connect(url: string | URLObject = this.#options.url ?? "redis://localhost:6379"): Promise { if (this.#open) return this; if (typeof url === "object") { @@ -73,18 +72,18 @@ export class Client>(definition: T, methods?: M, options?: SchemaOptions): Schema< + public schema, M extends MethodsDefinition<(T & SD)>>(definition: T, methods?: M, options?: SchemaOptions): Schema< { [K in keyof (T & SD)]: (T & SD)[K] }, { [K in keyof (M & MD)]: (M & MD)[K] } > { return new Schema({ - ...this._options.inject?.schema?.definition, + ...this.#options.inject?.schema?.definition, ...definition }, { - ...this._options.inject?.schema?.methods, + ...this.#options.inject?.schema?.methods, ...methods }, { - ...this._options.inject?.schema?.options, + ...this.#options.inject?.schema?.options, ...options }); } @@ -120,6 +119,20 @@ export class Client { + return this.#options; + } + + public set options(options: ClientOptions) { + if (this.#open) { + throw new PrettyError("Client options cannot be modified when the client is connected", { + reference: "redis-om" + }); + } + + this.#options = { ...this.#options, ...options }; + } + public set redisClient(client: NodeRedisClient) { if (!this.#open) { this.#client = client; diff --git a/src/document/document-helpers/general-helpers.ts b/src/document/document-helpers/general-helpers.ts index 3ac5d4b..3369f7a 100644 --- a/src/document/document-helpers/general-helpers.ts +++ b/src/document/document-helpers/general-helpers.ts @@ -2,7 +2,7 @@ import { PrettyError } from "@infinite-fansub/logger"; import { inspect } from "node:util"; import { Color } from "colours.js"; -import type { ObjectField, ParseSchema } from "../../typings"; +import type { ParsedFieldType, ParsedSchemaDefinition } from "../../typings"; import type { HASHDocument } from "../hash-document"; import type { JSONDocument } from "../json-document"; @@ -16,25 +16,9 @@ export function numberToDate(val: number): Date { return new Date(val); } -export function stringsToObject(arr: Array, val: unknown): Record { - let obj: any = {}; - arr.reduce((object, accessor, i) => { - object[accessor] = {}; - - if (arr.length - 1 === i) { - object[accessor] = val; - } - - return >object[accessor]; - }, obj); - - return >obj; -} - export function validateSchemaReferences( - this: HASHDocument | JSONDocument, - schema: ParseSchema["references"], - data: JSONDocument | ParseSchema["references"] = this + schema: ParsedSchemaDefinition["references"], + data: JSONDocument | HASHDocument ): void { for (let i = 0, keys = Object.keys(schema), len = keys.length; i < len; i++) { const key = keys[i]; @@ -49,7 +33,7 @@ export function validateSchemaReferences( reference: "redis-om", lines: [ { - marker: { text: "Content:" }, + marker: { text: "Content: " }, error: `Reference 'ids' must be strings.\nFound type: '${typeof dataVal[i]}'` } ] @@ -60,102 +44,110 @@ export function validateSchemaReferences( } export function validateSchemaData( - this: HASHDocument | JSONDocument, - schema: ParseSchema["data"], - data: HASHDocument | ParseSchema["data"] = this, - isField: boolean = false + schema: ParsedSchemaDefinition["data"], + data: JSONDocument | HASHDocument ): void { - for (let i = 0, entries = Object.entries(schema), len = entries.length; i < len; i++) { - const [key, value] = entries[i]; + for (let i = 0, entries = Object.entries(schema), length = entries.length; i < length; i++) { + const [key, field] = entries[i]; - if (isField && typeof data[key] === "undefined") throw new PrettyError(`Found a missing property inside an object: '${key}'`, { + validate(field, data[key], key); + } +} + +function validate( + field: ParsedFieldType, + value: any, + workingKey: string +): void { + if (value === null) throw new PrettyError("Cannot save 'null' to the database", { + reference: "redis-om" + }); + + if (value.length === 0 || Object.keys(value).length === 0) { + if (typeof value !== "undefined") return; + if (field.optional) return; + if (typeof field.default === "undefined") throw new PrettyError(`'${workingKey}' is required but was not given a value`, { reference: "redis-om" }); - const dataVal = data[key]; + } - if (dataVal === null) throw new PrettyError("Cannot save 'null' to the database", { + if (field.type === "object") { + if (field.properties === null) return; + validateSchemaData(field.properties, value); + } else if (field.type === "array") { + for (let i = 0, len = value.length; i < len; i++) { + const val = value[i]; + + if (typeof field.elements === "object") { + validateSchemaData(field.elements, val); + } else { + // This should work without problems... + validate({ type: field.elements }, val, `${workingKey}.${i}`); + } + } + } else if (field.type === "tuple") { + for (let i = 0, length = field.elements.length; i < length; i++) { + validate(field.elements[i], value[i], `${workingKey}.${i}`); + } + } else if (field.type === "date") { + if (!(value instanceof Date) && typeof value !== "number") throw new PrettyError(`Expected 'Date' or type 'number' but instead got ${typeof value}`, { reference: "redis-om" }); - - if (typeof dataVal === "undefined" && value.optional) continue; - if (typeof dataVal === "undefined" && !value.optional && typeof value.default === "undefined") throw new PrettyError(`'${key}' is required but was not given a value`, { + } else if (field.type === "point") { + if (typeof value !== "object") throw new PrettyError("Invalid 'point' format", { + reference: "redis-om", + lines: [ + { + error: inspect({ + longitude: "number", + latitude: "number" + }, { colors: true }), + marker: { text: "Expected:", color: Color.fromHex("#00FF00"), spacedBefore: true, newLine: true } + }, + { + error: typeof value, + marker: { text: "Got: ", color: Color.fromHex("#00FF00"), spacedBefore: true } + } + ] + }); + if (!value.longitude || !value.latitude) throw new PrettyError("'longitude' or 'latitude' where not defined", { reference: "redis-om" }); - - if (value.type === "object") { - if (!(value).properties) continue; - //@ts-expect-error Typescript is getting confused due to the union of array and object - validateSchemaData(value.properties, dataVal, true); - } else if (value.type === "array") { - dataVal.every((val: unknown) => { - if (typeof val === "object") return; - //@ts-expect-error Typescript is getting confused due to the union of array and object - if (value.elements === "text") { - if (typeof val !== "string") throw new PrettyError(`Invalid text received. Expected type: 'string' got '${typeof val}'`, { - reference: "redis-om" - }); - return; + if (Object.keys(value).length > 2) throw new PrettyError("Invalid 'point' format", { + reference: "redis-om", + lines: [ + { + error: inspect({ + longitude: "number", + latitude: "number" + }, { colors: true }), + marker: { text: "Expected:", color: Color.fromHex("#00FF00"), spacedBefore: true, newLine: true } + }, + { + error: inspect(value, { colors: true }), + marker: { text: "Got:", color: Color.fromHex("#00FF00"), spacedBefore: true, newLine: true } } - //@ts-expect-error Typescript is getting confused due to the union of array and object - if (typeof val !== value.elements) throw new PrettyError(`Got wrong type on array elements. Expected type: '${value.elements}' got '${typeof val}'`, { + ] + }); + } else if (field.type === "text") { + if (typeof value !== "string") throw new PrettyError("Text field has to be a string"); + } else if (field.type === "vector") { + if (!(value instanceof Float32Array) && !(value instanceof Float64Array) && !Array.isArray(value)) throw new PrettyError("Got wrong vector format"); + } else if (field.type === "string" || field.type === "number" || field.type === "bigint") { + if (typeof field.literal !== "undefined") { + if (!field.literal.includes(value)) { + throw new PrettyError(`Got wrong value. Expected one of: '${field.literal}' got '${value}'`, { reference: "redis-om" }); - }); - - } else if (value.type === "tuple") { - //@ts-expect-error Typescript is getting confused due to the union of array and object - for (let j = 0, le = value.elements.length; j < le; j++) { - //@ts-expect-error Typescript is getting confused due to the union of array and object - validateSchemaData({ [j]: value.elements[j] }, { [j]: dataVal[j] }); } - } else if (value.type === "date") { - if (!(dataVal instanceof Date) && typeof dataVal !== "number") throw new PrettyError(`Expected 'Date' or type 'number' but instead got ${typeof dataVal}`, { - reference: "redis-om" - }); - } else if (value.type === "point") { - if (typeof dataVal !== "object") throw new PrettyError("Invalid 'point' format", { - reference: "redis-om", - lines: [ - { - error: inspect({ - longitude: "number", - latitude: "number" - }, { colors: true }), - marker: { text: "Expected:", color: Color.fromHex("#00FF00"), spacedBefore: true, newLine: true } - }, - { - error: typeof dataVal, - marker: { text: "Got: ", color: Color.fromHex("#00FF00"), spacedBefore: true } - } - ] - }); - if (!dataVal.longitude || !dataVal.latitude) throw new PrettyError("'longitude' or 'latitude' where not defined", { + + } else if (typeof value !== field.type) { + throw new PrettyError(`Got wrong value type. Expected type: '${field.type}' got '${typeof value}'`, { reference: "redis-om" }); - if (Object.keys(dataVal).length > 2) throw new PrettyError("Invalid 'point' format", { - reference: "redis-om", - lines: [ - { - error: inspect({ - longitude: "number", - latitude: "number" - }, { colors: true }), - marker: { text: "Expected:", color: Color.fromHex("#00FF00"), spacedBefore: true, newLine: true } - }, - { - error: inspect(dataVal, { colors: true }), - marker: { text: "Got:", color: Color.fromHex("#00FF00"), spacedBefore: true, newLine: true } - } - ] - }); - } else if (value.type === "text") { - if (typeof dataVal !== "string") throw new PrettyError("Text field has to be a string"); - } else if (value.type === "vector") { - if (!(dataVal instanceof Float32Array) && !(dataVal instanceof Float64Array) && !Array.isArray(dataVal)) throw new PrettyError("Got wrong vector format"); - } else { - // This handles `number`, `boolean` and `string` types - if (typeof dataVal !== value.type) throw new PrettyError(`Got wrong value type. Expected type: '${value.type}' got '${typeof dataVal}'`); } + } else /* Handles `boolean` */ { + if (typeof value !== field.type) throw new PrettyError(`Got wrong value type. Expected type: '${field.type}' got '${typeof value}'`); } } \ No newline at end of file diff --git a/src/document/document-helpers/hash-helpers.ts b/src/document/document-helpers/hash-helpers.ts index 8d37121..1edcd30 100644 --- a/src/document/document-helpers/hash-helpers.ts +++ b/src/document/document-helpers/hash-helpers.ts @@ -1,132 +1,142 @@ -import { dateToNumber, numberToDate, stringsToObject } from "./general-helpers"; +import { PrettyError } from "@infinite-fansub/logger"; -import type { ArrayField, BaseField, ObjectField, Point } from "../../typings"; +import { dateToNumber, numberToDate } from "./general-helpers"; -export function booleanToString(val: boolean): string { - return (+val).toString(); -} +import type { ParsedFieldType, ParsedSchemaDefinition } from "../../typings"; -export function pointToString(val: Point): string { - const { longitude, latitude } = val; - return `${longitude},${latitude}`; -} +export function documentFieldToHASHValue(field: ParsedFieldType | { type: ParsedFieldType["type"] }, value: any, key?: string): Array { + if (field.type === "boolean") return keyExists(booleanToString(value), key); + if (field.type === "date") return keyExists(dateToNumber(value).toString(), key); + if (field.type === "point") return keyExists(`${value.longitude},${value.latitude}`, key); + if (field.type === "vector") return keyExists(Buffer.from(value).toString(), key); + if (field.type === "object") { + if (!("properties" in field) || field.properties === null) return keyExists(JSON.stringify(value), key); + if (!key) throw new PrettyError("Something went terribly wrong"); + return flatten(field.properties, value, key); + } -export function stringToBoolean(val: string): boolean { - return !!+val; -} + if (field.type === "array") { + if (!("elements" in field)) return keyExists(value.toString(), key); + const temp: Array = []; -export function stringToPoint(val: string): Point { - const [longitude, latitude] = val.split(","); - return { longitude: parseFloat(longitude), latitude: parseFloat(latitude) }; -} + if (typeof field.elements === "object") { + for (let i = 0, length = value.length; i < length; i++) { + temp.push(...flatten(field.elements, value[i], `${key}.${i}`)); + } -export function stringToNumber(val: string): number { - return parseFloat(val); -} + return temp; + } -export function hashFieldToString(schema: BaseField, val: any): string { - if (schema.type === "boolean") { - return booleanToString(val); - } else if (schema.type === "date") { - return dateToNumber(val).toString(); - } else if (schema.type === "point") { - return pointToString(val); - } else if (schema.type === "array") { - const temp = []; - for (let i = 0, len = (>val).length; i < len; i++) { - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - temp.push(hashFieldToString({ type: (schema).elements ?? "string" }, val[i])); + for (let i = 0, length = value.length; i < length; i++) { + const parsed = documentFieldToHASHValue({ type: field.elements }, value[i], `${key}.${i}`); + temp.push(parsed.length > 1 ? parsed[1] : parsed[0]); } - return temp.join((schema).separator); - } else if (schema.type === "vector") { - return Buffer.from(val).toString(); + + return keyExists(temp.join(field.separator), key); } - return val.toString(); + if (field.type === "tuple") { + if (!("elements" in field)) return keyExists(value.toString(), key); + + const tempField: Record<`${number}`, ParsedFieldType> = { ...field.elements }; + const tempValue = { ...value }; + return flatten(tempField, tempValue, key); + } + + return keyExists(value.toString(), key); } -export function objectToHashString(data: Record, k: string, schema?: Record): Array { - const init: Array = []; - for (let i = 0, entries = Object.entries(data), len = entries.length; i < len; i++) { - const [key, val] = entries[i]; +function flatten(field: ParsedSchemaDefinition["data"], value: any, key?: string): Array { + const temp: Array = []; - if (typeof val === "object" && !Array.isArray(val) && !(val instanceof Date)) { - if (typeof schema?.[key]?.properties !== "undefined") { - init.push(...objectToHashString(val, `${k}.${key}`, schema[key].properties)); - continue; - } - init.push(...objectToHashString(val, `${k}.${key}`)); - continue; - } + for (let i = 0, entries = Object.entries(field), length = entries.length; i < length; i++) { + const [k, val] = entries[i]; - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - init.push(`${k}.${key}`, hashFieldToString(schema?.[key] ?? convertUnknownToSchema(val), val) ?? ""); + temp.push(...documentFieldToHASHValue(val, value[k], key ? `${key}.${k}` : k)); } - return init; + return temp; } -export function convertUnknownToSchema(val: any): BaseField { - if (Array.isArray(val)) return { type: "array" }; - if (val instanceof Date) return { type: "date" }; - if (typeof val === "object" && "latitude" in val && "longitude" in val) return { type: "point" }; - return { type: <"string" | "number" | "boolean" | "object">typeof val }; +function keyExists(value: string, key: string | undefined): Array { + return key ? [key, value] : [value]; } -export function stringToHashField(schema: BaseField, val: string): any { - if (schema.type === "number") { - return stringToNumber(val); - } if (schema.type === "boolean") { - return stringToBoolean(val); - } else if (schema.type === "date") { - return numberToDate(stringToNumber(val)); - } else if (schema.type === "point") { - return stringToPoint(val); - } else if (schema.type === "array") { - const temp = val.split((schema).separator ?? ","); - for (let i = 0, len = temp.length; i < len; i++) { - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - temp[i] = stringToHashField({ type: (schema).elements ?? "string" }, temp[i]); - } - return temp; - } else if (schema.type === "vector") { - //@ts-expect-error Type overload - if (schema.vecType === "FLOAT32") return new Float32Array(val); - //@ts-expect-error Type overload - if (schema.vecType === "FLOAT64") return new Float64Array(val); +export function HASHValueToDocumentField( + field: ParsedFieldType | { type: ParsedFieldType["type"] }, + value: any, + existingValue?: any, + keysList?: Array +): unknown { + if (field.type === "number") return parseFloat(value); + if (field.type === "bigint") return BigInt(value); + if (field.type === "boolean") return stringToBoolean(value); + if (field.type === "date") return numberToDate(parseFloat(value)); + if (field.type === "point") { + const [longitude, latitude] = value.split(","); + return { longitude: +longitude, latitude: +latitude }; } - return val; -} -export function stringToHashArray(arr: Array, schema: any, val: string): Record { - let temp: any = []; - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const idx = arr.shift()!; - let props = schema[idx].properties; + if (field.type === "vector") { + if (!("vecType" in field) || field.vecType === "FLOAT32") return new Float32Array(Buffer.from(value)); + return new Float64Array(Buffer.from(value)); + } - if (arr.length === 1) { - return { [arr[0]]: val }; + if (field.type === "object") { + if (!("properties" in field) || field.properties === null) return JSON.parse(value); + if (!existingValue && !keysList) throw new PrettyError("Something went terribly wrong"); + + return deepMerge(existingValue ?? {}, arrayOfKeysToObject(keysList, value)); + } + + if (field.type === "array") { + if (!("elements" in field)) return value; + if (typeof field.elements === "object") { + const temp: Array = []; + if (!existingValue || !keysList) throw new PrettyError("Something went terribly wrong"); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const index = keysList.shift()!; + + for (let i = 0, length = temp.length; i < length; i++) { + temp[i] = HASHValueToDocumentField(field.elements[+index], value, existingValue, keysList); + } + + return temp; + } else { + const splitValue = value.split(field.separator); + for (let i = 0, length = splitValue.length; i < length; i++) { + splitValue[i] = HASHValueToDocumentField({ type: field.elements }, splitValue[i]); + } + + return splitValue; + } } - for (let i = 0, len = arr.length; i < len; i++) { - const value = arr[i]; + if (field.type === "tuple") { + if (!("elements" in field)) return value; + if (!existingValue && !keysList) throw new PrettyError("Something went terribly wrong"); + if (typeof existingValue === "undefined") existingValue = new Array(field.elements.length); - if (props[value].type === "object") { - temp.push(value); - const x = i + 1; - props = { [arr[x]]: props[value].properties[arr[x]] }; - continue; + let index = +(keysList?.shift() ?? 0); + let currentField = field.elements[index]; + + if (typeof currentField === "undefined") { + index = +(keysList?.shift() ?? 0); + currentField = field.elements[index]; } - temp.push(value, stringToHashField(props[value], val)); + existingValue[index] = HASHValueToDocumentField(currentField, value, existingValue, keysList); + + return existingValue; } - const trueVal = temp.pop(); - return stringsToObject(temp, trueVal); + return value; + } -export function deepMerge(...objects: Array>): Record { +// This needs to be optimized +function deepMerge(...objects: Array>): Record { let newObject: Record = {}; for (let i = 0, len = objects.length; i < len; i++) { @@ -149,25 +159,26 @@ export function deepMerge(...objects: Array>): Record, key: string): BaseField | undefined { - if (!data.properties) return { type: "string" }; - for (let i = 0, entries = Object.entries(data.properties), len = entries.length; i < len; i++) { - const [k, value] = entries[i]; +// This also needs to be optimized +export function arrayOfKeysToObject(arr: Array | undefined, val: unknown): Record { + const obj: Record = {}; + arr?.reduce((object, accessor, i) => { + object[accessor] = {}; - if (key === k) { - return value; + if (arr.length - 1 === i) { + object[accessor] = val; } - if (typeof value === "undefined") continue; + return >object[accessor]; + }, obj); - //@ts-expect-error I dont have a proper type for this - if (value.type === "object") { - //@ts-expect-error I dont have a proper type for this - return getLastKeyInSchema(value, key); - } + return obj; +} - continue; - } +function booleanToString(val: boolean): string { + return (+val).toString(); +} - return void 0; +function stringToBoolean(val: string): boolean { + return !!+val; } \ No newline at end of file diff --git a/src/document/document-helpers/json-helpers.ts b/src/document/document-helpers/json-helpers.ts index 7830f57..4b285f0 100644 --- a/src/document/document-helpers/json-helpers.ts +++ b/src/document/document-helpers/json-helpers.ts @@ -1,122 +1,117 @@ import { dateToNumber, numberToDate } from "./general-helpers"; -import type { ArrayField, BaseField, ObjectField } from "../../typings"; - -export function jsonFieldToDoc(schema: BaseField, val: any): any { - if (schema.type === "date") { - return numberToDate(val); - } else if (schema.type === "point") { - const [longitude, latitude] = val.split(","); - return { longitude: +longitude, latitude: +latitude }; - } else if (schema.type === "object") { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - parseJsonObject(schema, val); - } else if (schema.type === "array") { - for (let i = 0, le = val.length; i < le; i++) { - val[i] = jsonFieldToDoc({ type: (schema).elements }, val[i]); - } - return val; - } else if (schema.type === "vector") { - //@ts-expect-error Type overload - if (schema.vecType === "FLOAT32") return new Float32Array(val); - //@ts-expect-error Type overload - if (schema.vecType === "FLOAT64") return new Float64Array(val); +import type { ParsedFieldType, ParsedSchemaDefinition } from "../../typings"; + +export function documentFieldToJSONValue(field: ParsedFieldType | { type: ParsedFieldType["type"] }, value: any): unknown { + if (field.type === "bigint") return value.toString(); + if (field.type === "date") return dateToNumber(value); + if (field.type === "point") return `${value.longitude},${value.latitude}`; + if (field.type === "vector") return Array.from(value); + if (field.type === "object") { + if (!("properties" in field) || field.properties === null) return value; + return transformParsedDefinition(field.properties, value, documentFieldToJSONValue); } - return val; -} + if (field.type === "array") { + if (!("elements" in field)) return value; + for (let i = 0, length = value.length; i < length; i++) { + if (typeof field.elements === "object") { + value[i] = transformParsedDefinition(field.elements, value[i], documentFieldToJSONValue); + continue; + } -export function docToJson(schema: BaseField, val: any): any { - if (schema.type === "date") { - return dateToNumber(val); - } else if (schema.type === "point") { - return `${val.longitude},${val.latitude}`; - } else if (schema.type === "object") { - parseDoc(schema, val); - } else if (schema.type === "array") { - for (let i = 0, le = val.length; i < le; i++) { - val[i] = docToJson({ type: (schema).elements }, val[i]); + value[i] = documentFieldToJSONValue({ type: field.elements }, value[i]); } - return val; - } else if (schema.type === "vector") { - return Array.from(val); + + return value; } - return val; + if (field.type === "tuple") { + if (!("elements" in field)) return value; + if (field.index) { + const tempField: Record = { ...field.elements }; + const tempValue = { ...value }; -} + for (let i = 0, entries = Object.entries(tempField), length = entries.length; i < length; i++) { + const val = entries[i][1]; -export function parseDoc(schema: Required, val: any): any { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - for (let i = 0, entries = Object.entries((schema).properties!), len = entries.length; i < len; i++) { - const [key, value] = entries[i]; + tempValue[i] = documentFieldToJSONValue(val, tempValue[i]); + } - //@ts-expect-error I dont have a proper type for this - if (value.type === "object") { - //@ts-expect-error I dont have a proper type for this - val[key] = parseDoc(value, val[key]); + return tempValue; } - //@ts-expect-error I dont have a proper type for this - val[key] = docToJson(value, val[key]); + for (let i = 0, length = field.elements.length; i < length; i++) { + value[i] = documentFieldToJSONValue(field.elements[i], value[i]); + } + + return value; } - return val; + return value; } -export function parseJsonObject(schema: Required, val: any): any { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - for (let i = 0, entries = Object.entries((schema).properties!), len = entries.length; i < len; i++) { - const [key, value] = entries[i]; +function transformParsedDefinition( + field: ParsedSchemaDefinition["data"], + value: any, + transformer: typeof documentFieldToJSONValue | typeof JSONValueToDocumentField +): Record { + const temp: Record = {}; - //@ts-expect-error I dont have a proper type for this - if (value.type === "object") { - //@ts-expect-error I dont have a proper type for this - val[key] = parseJsonObject(value, val[key]); - } + for (let i = 0, entries = Object.entries(field), length = entries.length; i < length; i++) { + const [key, val] = entries[i]; + if (typeof value[key] === "undefined") continue; - //@ts-expect-error I dont have a proper type for this - val[key] = jsonFieldToDoc(value, val[key]); + temp[key] = transformer(val, value[key]); } - return val; + return temp; } -export function objectToString(val: Record, k: string): Array> { - const arr: Array> = []; - - for (let i = 0, entries = Object.entries(val), len = entries.length; i < len; i++) { - const [key, value] = entries[i]; +export function JSONValueToDocumentField(field: ParsedFieldType | { type: ParsedFieldType["type"] }, value: any): unknown { + if (field.type === "bigint") return BigInt(value); + if (field.type === "date") return numberToDate(value); + if (field.type === "point") { + const [longitude, latitude] = value.split(","); + return { longitude: +longitude, latitude: +latitude }; + } - if (typeof value === "object") { - const temp = objectToString(value, `${k}.${key}`); - arr.push(...temp); - continue; - } + if (field.type === "vector") { + if (!("vecType" in field) || field.vecType === "FLOAT32") return new Float32Array(value); + return new Float64Array(value); + } - arr.push({ [`${k}.${key}`]: value }); + if (field.type === "object") { + if (!("properties" in field) || field.properties === null) return value; + return transformParsedDefinition(field.properties, value, JSONValueToDocumentField); } - return arr; -} + if (field.type === "array") { + if (!("elements" in field)) return value; + for (let i = 0, length = value.length; i < length; i++) { + if (typeof field.elements === "object") { + value[i] = transformParsedDefinition(field.elements, value[i], JSONValueToDocumentField); + continue; + } -export function tupleToObjStrings(val: Array, key: string): Array> { - const arr: Array> = []; + value[i] = JSONValueToDocumentField({ type: field.elements }, value[i]); + } - for (let i = 0, len = val.length; i < len; i++) { - const value = val[i]; + return value; + } - if (typeof value === "object") { - for (let j = 0, entries = Object.entries(value), le = entries.length; j < le; j++) { - const [k, v] = entries[j]; + if (field.type === "tuple") { + if (!("elements" in field)) return value; + if (field.index) { + value = Object.values(value); + } - arr.push({ [`${key}.${i}.${k}`]: v }); - } - continue; + for (let i = 0, length = field.elements.length; i < length; i++) { + value[i] = JSONValueToDocumentField(field.elements[i], value[i]); } - arr.push({ [`${key}.${i}`]: value }); + return value; } - return arr; + return value; } \ No newline at end of file diff --git a/src/document/hash-document.ts b/src/document/hash-document.ts index 52f4715..e1b7ec5 100644 --- a/src/document/hash-document.ts +++ b/src/document/hash-document.ts @@ -4,33 +4,28 @@ import { randomUUID } from "node:crypto"; import { ReferenceArray } from "../utils"; import { validateSchemaReferences, - validateSchemaData, - objectToHashString, - getLastKeyInSchema, - tupleToObjStrings, - hashFieldToString, - stringToHashField, - stringToHashArray, - stringsToObject, - deepMerge + documentFieldToHASHValue, + HASHValueToDocumentField, + validateSchemaData } from "./document-helpers"; -import type { DocumentShared, ObjectField, ParseSchema } from "../typings"; +import type { DocumentShared, ParsedSchemaDefinition } from "../typings"; +import { PrettyError } from "@infinite-fansub/logger"; export class HASHDocument implements DocumentShared { - readonly #schema: ParseSchema; + readonly #schema: ParsedSchemaDefinition; readonly #validate: boolean; readonly #autoFetch: boolean; #validateSchemaReferences = validateSchemaReferences; #validateSchemaData = validateSchemaData; - public readonly $global_prefix: string; - public readonly $prefix: string; - public readonly $model_name: string; - public readonly $suffix: string | undefined; - public readonly $id: string; - public readonly $record_id: string; + readonly #global_prefix: string; + readonly #prefix: string; + readonly #model_name: string; + readonly #suffix: string | undefined; + readonly #id: string; + readonly #record_id: string; /* * Using any so everything works as intended @@ -40,7 +35,7 @@ export class HASHDocument implements DocumentShared { [key: string]: any; public constructor( - schema: ParseSchema, + schema: ParsedSchemaDefinition, record: { globalPrefix: string, prefix: string, @@ -53,12 +48,12 @@ export class HASHDocument implements DocumentShared { validate: boolean = true, wasAutoFetched: boolean = false ) { - this.$global_prefix = record.globalPrefix; - this.$prefix = record.prefix; - this.$model_name = record.name; - this.$suffix = data?.$suffix ?? (typeof record.suffix === "function" ? record.suffix() : record.suffix); - this.$id = data?.$id ?? record.id ?? randomUUID(); - this.$record_id = `${this.$global_prefix}:${this.$prefix}:${this.$model_name}:${this.$suffix ? `${this.$suffix}:` : ""}${this.$id}`; + this.#global_prefix = record.globalPrefix; + this.#prefix = record.prefix; + this.#model_name = record.name; + this.#suffix = data?.$suffix ?? (typeof record.suffix === "function" ? record.suffix() : record.suffix); + this.#id = data?.$id?.toString() ?? record.id ?? randomUUID(); + this.#record_id = `${this.#global_prefix}:${this.#prefix}:${this.#model_name}:${this.#suffix ? `${this.#suffix}:` : ""}${this.#id}`; this.#schema = schema; this.#validate = validate; this.#autoFetch = wasAutoFetched; @@ -72,51 +67,65 @@ export class HASHDocument implements DocumentShared { if (key.startsWith("$")) continue; const arr = key.split("."); - if (arr.length > 1) /* This is an object or tuple */ { - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - if (schema.data[arr[0]]?.type === "tuple") { - // var name - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const temp = arr.shift()!; - - if (arr.length === 1) { - this[temp].push(stringToHashField( - //@ts-expect-error Type overload - schema.data[temp].elements[arr[0]], - value + if (arr.length > 1) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const keyName = arr.shift()!; + const workingField = schema.data[keyName]; + + if (typeof workingField !== "undefined") { + if (workingField.type === "tuple") { + if (!Array.isArray(this[keyName])) this[keyName] = []; + if (workingField.elements[+arr[0]].type === "object") { + this[keyName][+arr[0]] = (HASHValueToDocumentField( + workingField.elements[+arr[0]], + value, + this[keyName][+arr[0]], + arr + ))[+arr[0]]; + } else { + this[keyName][+arr[0]] = HASHValueToDocumentField( + workingField.elements[+arr[0]], + value, + this[keyName][+arr[0]], + arr + ); + } + } else if (workingField.type === "object") { + if (workingField.properties === null) throw new PrettyError("Something went terribly wrong"); + if (typeof this[keyName] === "undefined") this[keyName] = {}; + this[keyName][arr[0]] = HASHValueToDocumentField( + workingField.properties[arr[0]], + value, + this[keyName][arr[0]], + arr + ); + } else if (workingField.type === "array") { + if (!Array.isArray(this[keyName])) this[keyName] = []; + if (typeof workingField.elements !== "object") throw new PrettyError("Something went terribly wrong processing an array"); + this[keyName] = Object.values(HASHValueToDocumentField( + { type: "object", properties: workingField.elements }, + value, + this[keyName], + arr )); - - continue; } - - //@ts-expect-error Type overload - this[temp].push(stringToHashArray(arr, schema.data[temp].elements, value)); - } else /*we assume its an object*/ { - this[arr[0]] = deepMerge( - this[arr[0]], - stringsToObject( - arr, - stringToHashField( - getLastKeyInSchema(>schema.data[arr[0]], arr.at(-1)) ?? { type: "string" }, - value - ) - )[arr[0]] - ); + continue; } - continue; } + if (typeof schema.data[key] !== "undefined") { + this[key] = HASHValueToDocumentField(schema.data[key], value); + continue; + } // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (schema.references[key] === null) { if (!this.#autoFetch) { - this[key] = new ReferenceArray(...>stringToHashField({ type: "array" }, value)); + this[key] = new ReferenceArray(value.split(" | ")); continue; } - this[key] = value; - continue; } - this[key] = stringToHashField(schema.data[key], value); + this[key] = value; } } else { for (let i = 0, entries = Object.entries(data), len = entries.length; i < len; i++) { @@ -133,16 +142,12 @@ export class HASHDocument implements DocumentShared { const [key, value] = entries[i]; this[key] = value.default ?? (value.type === "object" ? {} - : value.type === "tuple" + : value.type === "tuple" || value.type === "array" ? [] : value.type === "vector" - //@ts-expect-error Type overload ? value.vecType === "FLOAT32" ? new Float32Array() - //@ts-expect-error Type overload - : value.vecType === "FLOAT64" - ? new Float64Array() - : [] + : new Float64Array() : void 0); } @@ -152,8 +157,9 @@ export class HASHDocument implements DocumentShared { } } - public toString(): string { - if (this.#validate) this.#validateSchemaData(this.#schema.data); + /** This is actually and array... eventually i change it */ + public toString(): Array { + if (this.#validate) this.#validateSchemaData(this.#schema.data, this); const arr = [ "$id", @@ -162,48 +168,51 @@ export class HASHDocument implements DocumentShared { if (this.$suffix) arr.push("$suffix", this.$suffix); - for (let i = 0, entries = Object.entries(this.#schema.data), len = entries.length; i < len; i++) { + for (let i = 0, entries = Object.entries(this), length = entries.length; i < length; i++) { const [key, val] = entries[i]; + const schema = this.#schema.data[key]; - if (typeof this[key] === "undefined") continue; - - if (val.type === "object") { - //@ts-expect-error Typescript is getting confused due to the union of array and object - arr.push(...objectToHashString(this[key], key, val.properties)); - continue; - } else if (val.type === "tuple") { - const temp = tupleToObjStrings(this[key], key); - for (let j = 0, le = temp.length; j < le; j++) { - const [k, value] = Object.entries(temp[j])[0]; - - //@ts-expect-error Type Overload - if (val.elements[j].type === "object") { - //@ts-expect-error Type Overload - arr.push(...objectToHashString(value, k, val.elements[j].properties)); - continue; - } - - arr.push(k, hashFieldToString(val, value)); - } + if (typeof schema !== "undefined") { + arr.push(...documentFieldToHASHValue(schema, val, key)); continue; } - arr.push(key, hashFieldToString(val, this[key])); - + if (typeof this.#schema.references[key] === "undefined") arr.push(val); } if (!this.#autoFetch) { - if (this.#validate) this.#validateSchemaReferences(this.#schema.references); + if (this.#validate) this.#validateSchemaReferences(this.#schema.references, this); for (let i = 0, keys = Object.keys(this.#schema.references), len = keys.length; i < len; i++) { const key = keys[i]; - if (!this[key]?.length) continue; - arr.push(key, hashFieldToString({ type: "array" }, this[key])); + if (this[key].length > 0) arr.push(key, this[key].join(" | ")); } } - //@ts-expect-error pls dont question it - // eslint-disable-next-line @typescript-eslint/no-unsafe-return return arr; } + + public get $globalPrefix(): string { + return this.#global_prefix; + } + + public get $prefix(): string { + return this.#prefix; + } + + public get $model_name(): string { + return this.#model_name; + } + + public get $suffix(): string | undefined { + return this.#suffix; + } + + public get $id(): string { + return this.#id; + } + + public get $record_id(): string { + return this.#record_id; + } } \ No newline at end of file diff --git a/src/document/json-document.ts b/src/document/json-document.ts index 24fcead..4c3c019 100644 --- a/src/document/json-document.ts +++ b/src/document/json-document.ts @@ -4,28 +4,27 @@ import { randomUUID } from "node:crypto"; import { ReferenceArray } from "../utils"; import { validateSchemaReferences, - validateSchemaData, - tupleToObjStrings, - jsonFieldToDoc, - docToJson + documentFieldToJSONValue, + JSONValueToDocumentField, + validateSchemaData } from "./document-helpers"; -import type { DocumentShared, ParseSchema } from "../typings"; +import type { DocumentShared, ParsedSchemaDefinition } from "../typings"; export class JSONDocument implements DocumentShared { - readonly #schema: ParseSchema; + readonly #schema: ParsedSchemaDefinition; readonly #validate: boolean; readonly #autoFetch: boolean; #validateSchemaReferences = validateSchemaReferences; #validateSchemaData = validateSchemaData; - public readonly $global_prefix: string; - public readonly $prefix: string; - public readonly $model_name: string; - public readonly $suffix: string | undefined; - public readonly $id: string; - public readonly $record_id: string; + readonly #global_prefix: string; + readonly #prefix: string; + readonly #model_name: string; + readonly #suffix: string | undefined; + readonly #id: string; + readonly #record_id: string; /* * Using any so everything works as intended @@ -35,7 +34,7 @@ export class JSONDocument implements DocumentShared { [key: string]: any; public constructor( - schema: ParseSchema, + schema: ParsedSchemaDefinition, record: { globalPrefix: string, prefix: string, @@ -48,12 +47,12 @@ export class JSONDocument implements DocumentShared { validate: boolean = true, wasAutoFetched: boolean = false ) { - this.$global_prefix = record.globalPrefix; - this.$prefix = record.prefix; - this.$model_name = record.name; - this.$suffix = data?.$suffix ?? (typeof record.suffix === "function" ? record.suffix() : record.suffix); - this.$id = data?.$id ?? record.id ?? randomUUID(); - this.$record_id = `${this.$global_prefix}:${this.$prefix}:${this.$model_name}:${this.$suffix ? `${this.$suffix}:` : ""}${this.$id}`; + this.#global_prefix = record.globalPrefix; + this.#prefix = record.prefix; + this.#model_name = record.name; + this.#suffix = data?.$suffix ?? (typeof record.suffix === "function" ? record.suffix() : record.suffix); + this.#id = data?.$id?.toString() ?? record.id ?? randomUUID(); + this.#record_id = `${this.#global_prefix}:${this.#prefix}:${this.#model_name}:${this.#suffix ? `${this.#suffix}:` : ""}${this.#id}`; this.#schema = schema; this.#validate = validate; this.#autoFetch = wasAutoFetched; @@ -65,27 +64,9 @@ export class JSONDocument implements DocumentShared { for (let i = 0, entries = Object.entries(data), len = entries.length; i < len; i++) { const [key, value] = entries[i]; if (key.startsWith("$")) continue; - const arr = key.split("."); - - if (arr.length > 1) /* This is a tuple */ { - - // var name - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const temp = arr.shift()!; - - if (arr.length === 1) { - //@ts-expect-error Type overload - this[temp].push(jsonFieldToDoc(schema.data[temp].elements[arr[0]], value)); - continue; - } - - //@ts-expect-error Type overload - this[temp].push({ [arr[1]]: jsonFieldToDoc(schema.data[temp].elements[arr[0]].properties[arr[1]], value) }); - continue; - } if (typeof schema.data[key] !== "undefined") { - this[key] = jsonFieldToDoc(schema.data[key], value); + this[key] = JSONValueToDocumentField(schema.data[key], value); continue; } @@ -112,16 +93,12 @@ export class JSONDocument implements DocumentShared { const [key, value] = entries[i]; this[key] = value.default ?? (value.type === "object" ? {} - : value.type === "tuple" + : value.type === "tuple" || value.type === "array" ? [] : value.type === "vector" - //@ts-expect-error Type overload ? value.vecType === "FLOAT32" ? new Float32Array() - //@ts-expect-error Type overload - : value.vecType === "FLOAT64" - ? new Float64Array() - : [] + : new Float64Array() : void 0); } @@ -132,48 +109,58 @@ export class JSONDocument implements DocumentShared { } public toString(): string { - if (this.#validate) this.#validateSchemaData(this.#schema.data); + if (this.#validate) this.#validateSchemaData(this.#schema.data, this); const obj: Record = { - $suffix: this.$suffix, - $id: this.$id + $suffix: this.#suffix, + $id: this.#id }; - for (let i = 0, entries = Object.entries(this.#schema.data), len = entries.length; i < len; i++) { + for (let i = 0, entries = Object.entries(this), length = entries.length; i < length; i++) { const [key, val] = entries[i]; + const schema = this.#schema.data[key]; - if (typeof this[key] === "undefined") continue; - - if (val.type === "tuple") { - const temp = tupleToObjStrings(this[key], key); - for (let j = 0, le = temp.length; j < le; j++) { - const [k, value] = Object.entries(temp[j])[0]; - - //@ts-expect-error Type overload - if (val.elements[j].type === "object") { - //@ts-expect-error Type overload - for (let u = 0, en = Object.entries(val.elements[j].properties), l = en.length; u < l; u++) { - const objV = en[u][1]; - obj[k] = docToJson(objV, value); - } - continue; - } - - obj[k] = value; - } + if (typeof schema !== "undefined") { + obj[key] = documentFieldToJSONValue(schema, val); continue; } - obj[key] = docToJson(val, this[key]); + if (typeof this.#schema.references[key] === "undefined") obj[key] = val; + } if (!this.#autoFetch) { - if (this.#validate) this.#validateSchemaReferences(this.#schema.references); + if (this.#validate) this.#validateSchemaReferences(this.#schema.references, this); for (let i = 0, keys = Object.keys(this.#schema.references), len = keys.length; i < len; i++) { const key = keys[i]; - obj[key] = this[key]; + if (this[key].length > 0) obj[key] = this[key]; } } + return JSON.stringify(obj, null); } + + public get $globalPrefix(): string { + return this.#global_prefix; + } + + public get $prefix(): string { + return this.#prefix; + } + + public get $model_name(): string { + return this.#model_name; + } + + public get $suffix(): string | undefined { + return this.#suffix; + } + + public get $id(): string { + return this.#id; + } + + public get $record_id(): string { + return this.#record_id; + } } \ No newline at end of file diff --git a/src/model.ts b/src/model.ts index de3245c..10cd9f0 100644 --- a/src/model.ts +++ b/src/model.ts @@ -1,7 +1,6 @@ import { PrettyError } from "@infinite-fansub/logger"; import { createHash } from "node:crypto"; -import { stringToHashField } from "./document/document-helpers"; import { JSONDocument, HASHDocument } from "./document"; import { methods, schemaData } from "./utils/symbols"; import { parseSchemaToSearchIndex } from "./utils"; @@ -13,10 +12,9 @@ import type { ReturnDocument, NodeRedisClient, ModelOptions, - VectorField, - MapSchema, ParsedMap, - Doc + MapSchema, + Document } from "./typings"; export class Model> { @@ -24,6 +22,7 @@ export class Model> { readonly #client: NodeRedisClient; readonly #searchIndexName: string; readonly #searchIndexHashName: string; + readonly #searchIndexBase: Array; readonly #searchIndex: Array; readonly #searchIndexHash: string; readonly #parsedSchema: ParsedMap; @@ -43,10 +42,12 @@ export class Model> { version: ver }; - this.#parsedSchema = parseSchemaToSearchIndex(this.#schema[schemaData].data); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const { map, index } = parseSchemaToSearchIndex(this.#schema[schemaData].data, this.#options.dataStructure!); + this.#parsedSchema = map; this.#searchIndexName = `${globalPrefix}:${this.#options.prefix}:${this.name}:index`; this.#searchIndexHashName = `${globalPrefix}:${this.#options.prefix}:${this.name}:index:hash`; - this.#searchIndex = [ + this.#searchIndexBase = [ "FT.CREATE", this.#searchIndexName, "ON", @@ -54,9 +55,18 @@ export class Model> { data.options.dataStructure!, "PREFIX", "1", - `${globalPrefix}:${this.#options.prefix}:${this.name}:`, - "SCHEMA" + `${globalPrefix}:${this.#options.prefix}:${this.name}:` ]; + + if (this.#options.language) this.#searchIndexBase.push("LANGUAGE", this.#options.language); + if (this.#options.stopWords) { + this.#searchIndexBase.push("STOPWORDS", this.#options.stopWords.length.toString()); + if (this.#options.stopWords.length > 0) this.#searchIndexBase.push(...this.#options.stopWords); + } + + this.#searchIndexBase.push("SCHEMA"); + + this.#searchIndex = index; this.#searchIndexHash = createHash("sha1").update(JSON.stringify({ name, structure: this.#options.dataStructure, @@ -69,7 +79,7 @@ export class Model> { else this.#docType = JSONDocument; } - public async get(id: string | number, autoFetch?: F): Promise | null> { + public async get(id: string | number, autoFetch?: F): Promise | undefined> { if (typeof id === "undefined") throw new PrettyError("A valid id was not given", { reference: "redis-om" }); @@ -87,12 +97,12 @@ export class Model> { const data = this.#options.dataStructure === "JSON" ? await this.#client.json.get(id.toString()) : await this.#client.hGetAll(id.toString()); - if (data === null) return null; + if (data === null) return void 0; if (autoFetch) { for (let i = 0, keys = Object.keys(this.#schema[schemaData].references), len = keys.length; i < len; i++) { const key = keys[i]; //@ts-expect-error node-redis types decided to die - const val = this.#options.dataStructure === "JSON" ? data[key] : stringToHashField({ type: "array" }, data[key]); + const val = this.#options.dataStructure === "JSON" ? data[key] : data[key].split(" | "); const temp = []; for (let j = 0, le = val.length; j < le; j++) { @@ -104,7 +114,7 @@ export class Model> { } } - return new this.#docType(this.#schema[schemaData], { + return new this.#docType(this.#schema[schemaData], { globalPrefix: this.#options.globalPrefix, prefix: this.#options.prefix, name: this.name @@ -115,7 +125,7 @@ export class Model> { public create(data?: { $id?: string | number } & MapSchema, true, true>): ReturnDocument; public create(idOrData?: string | number | { $id?: string | number } & MapSchema, true, true>): ReturnDocument { if (typeof idOrData === "object") { - return new this.#docType(this.#schema[schemaData], { + return new this.#docType(this.#schema[schemaData], { globalPrefix: this.#options.globalPrefix, prefix: this.#options.prefix, name: this.name, @@ -123,7 +133,7 @@ export class Model> { }, idOrData, false, this.#options.skipDocumentValidation, false); } - return new this.#docType(this.#schema[schemaData], { + return new this.#docType(this.#schema[schemaData], { globalPrefix: this.#options.globalPrefix, prefix: this.#options.prefix, name: this.name, @@ -132,7 +142,7 @@ export class Model> { }, void 0, false, this.#options.skipDocumentValidation, false); } - public async save(doc: Doc): Promise { + public async save(doc: Document): Promise { if (typeof doc === "undefined") throw new PrettyError("No document was passed to be save", { reference: "redis-om" }); @@ -140,24 +150,24 @@ export class Model> { // eslint-disable-next-line @typescript-eslint/no-base-to-string if (this.#options.dataStructure === "HASH") await this.#client.sendCommand(["HSET", doc.$record_id, ...doc.toString()]); // eslint-disable-next-line @typescript-eslint/no-base-to-string - else await this.#client.sendCommand(["JSON.SET", doc.$record_id, "$", doc.toString()]); + else await this.#client.sendCommand(["JSON.SET", doc.$record_id, "$", doc.toString()]); } - public async delete(...docs: Array): Promise { + public async delete(...docs: Array): Promise { if (!docs.length) throw new PrettyError("No documents were given to delete", { reference: "redis-om" }); await this.#client.del(this.#stringOrDocToString(docs)); } - public async exists(...docs: Array): Promise { + public async exists(...docs: Array): Promise { if (!docs.length) throw new PrettyError("No documents were given to check", { reference: "redis-om" }); return await this.#client.exists(this.#stringOrDocToString(docs)); } - public async expire(docs: Array, seconds: number | Date, mode?: "NX" | "XX" | "GT" | "LT"): Promise { + public async expire(docs: Array, seconds: number | Date, mode?: "NX" | "XX" | "GT" | "LT"): Promise { if (!docs.length) throw new PrettyError("No documents were given to expire", { reference: "redis-om" }); @@ -176,7 +186,7 @@ export class Model> { } public async createAndSave(data: { $id?: string | number } & MapSchema, true, true>): Promise { - const doc = new this.#docType(this.#schema[schemaData], { + const doc = new this.#docType(this.#schema[schemaData], { globalPrefix: this.#options.globalPrefix, prefix: this.#options.prefix, name: this.name, @@ -198,72 +208,16 @@ export class Model> { const currentIndexHash = await this.#client.get(this.#searchIndexHashName); if (currentIndexHash === this.#searchIndexHash) return; - await this.deleteIndex(); - - const prefix = this.#options.dataStructure === "JSON" ? "$." : ""; - - for (let i = 0, len = this.#parsedSchema.size, entries = [...this.#parsedSchema.entries()]; i < len; i++) { - const [key, val] = entries[i]; - const { path, value } = val; - let arrayPath = ""; - - if (this.#options.dataStructure === "JSON") { - if (value.type === "array") { - if (typeof value.elements !== "string") { - throw new PrettyError("Object definitions on `array` are not yet supported by the parser", { - reference: "redis-om" - }); - } - - arrayPath = value.elements === "text" ? "[*]" : value.elements === "number" || value.elements === "date" || value.elements === "point" ? "" : "*"; - } - } - - this.#searchIndex.push( - `${prefix}${key}${arrayPath}`, - "AS", - path, - value.type === "text" - ? "TEXT" - : value.type === "number" || value.type === "date" - ? "NUMERIC" - : value.type === "point" - ? "GEO" - : value.type === "vector" - ? "VECTOR" - : "TAG" - ); - - if (value.type === "vector") { - this.#searchIndex.push( - value.algorithm, - this.#getCount(value), - "TYPE", - value.vecType, - "DIM", - value.dim.toString(), - "DISTANCE_METRIC", - value.distance - ); - - if (value.cap) this.#searchIndex.push("INITIAL_CAP", value.cap.toString()); - - if (value.algorithm === "FLAT") { - if (value.size) this.#searchIndex.push("BLOCK_SIZE", value.size.toString()); - } else { - if (value.m) this.#searchIndex.push("M", value.m.toString()); - if (value.construction) this.#searchIndex.push("EF_CONSTRUCTION", value.construction.toString()); - if (value.runtime) this.#searchIndex.push("EF_RUNTIME", value.runtime.toString()); - if (value.epsilon) this.#searchIndex.push("EPSILON", value.epsilon.toString()); - } - } - - if (value.sortable) this.#searchIndex.push("SORTABLE"); + if (this.#searchIndex.length === 0) { + if (!this.#options.noLogs) console.log("Nothing to index... Skipping"); + return; } + await this.deleteIndex(); + await Promise.all([ this.#client.set(this.#searchIndexHashName, this.#searchIndexHash), - this.#client.sendCommand(this.#searchIndex) + this.#client.sendCommand([...this.#searchIndexBase, ...this.#searchIndex]) ]); } @@ -284,23 +238,7 @@ export class Model> { return await this.#client.ft.search(this.#searchIndexName, args.join(" ")); } - #getCount(value: VectorField): string { - let count = 6; - - if (value.cap) count += 2; - if (value.algorithm === "FLAT") { - if (value.size) count += 2; - } else { - if (value.m) count += 2; - if (value.construction) count += 2; - if (value.runtime) count += 2; - if (value.epsilon) count += 2; - } - - return count.toString(); - } - - #stringOrDocToString(stringOrNumOrDoc: Array): Array { + #stringOrDocToString(stringOrNumOrDoc: Array): Array { const temp = []; for (let i = 0, len = stringOrNumOrDoc.length; i < len; i++) { @@ -330,16 +268,16 @@ export class Model> { #defineMethods(): void { for (let i = 0, entries = Object.entries(this.#schema[methods]), len = entries.length; i < len; i++) { const [key, value] = entries[i]; - //@ts-expect-error Pending fix on type notations + //@ts-expect-error shenanigans this[key] = value; } } - public get getOptions(): ModelOptions { + public get options(): ModelOptions { return this.#options; } - public set mutateOptions(options: Partial) { + public set options(options: Partial) { this.#options = { ...this.#options, ...options }; } } \ No newline at end of file diff --git a/src/schema.ts b/src/schema.ts index 78f1017..c9a46af 100644 --- a/src/schema.ts +++ b/src/schema.ts @@ -5,11 +5,16 @@ import { Color } from "colours.js"; import { methods, schemaData } from "./utils/symbols"; import type { + ExtractSchemaDefinition, MethodsDefinition, SchemaDefinition, SchemaOptions, ParseSchema, - BaseField + NumberField, + StringField, + BaseField, + FieldType, + ParsedSchemaDefinition } from "./typings"; export class Schema = MethodsDefinition, P extends ParseSchema = ParseSchema> { @@ -17,17 +22,35 @@ export class Schema = /** @internal */ public [methods]: M; - /** @internal */ + /** + * @internal + * The real type is: {@link ParsedSchemaDefinition} + */ public [schemaData]: P; public constructor(rawData: S, methodsData?: M, public readonly options: SchemaOptions = {}) { - this[schemaData] = this.#parse(rawData); + this[schemaData] = this.#parse(rawData); this[methods] = methodsData ?? {}; this.options.dataStructure = options.dataStructure ?? "JSON"; } - #parse(schema: T): P { + /** This only extends the definition, it does not extend methods nor options */ + public extends, SD = ExtractSchemaDefinition>(schema: T): Schema & S> { + this[schemaData] = { + data: { + ...schema[schemaData].data, + ...this[schemaData].data + }, + references: { + ...schema[schemaData].references, + ...this[schemaData].references + } + }; + return this; + } + + #parse(schema: SchemaDefinition): ParsedSchemaDefinition { const data: Record = {}; const references: Record = {}; @@ -94,7 +117,7 @@ export class Schema = } else if (value === "tuple") { throw new PrettyError("Type 'tuple' needs to use its object definition"); } else if (value === "array") { - value = { type: value, elements: "string", default: undefined, optional: false, sortable: false, index: true }; + value = { type: value, elements: "string", default: undefined, optional: false, sortable: false, index: false, separator: "|" }; } else if (value === "vector") { value = { type: value, @@ -105,10 +128,11 @@ export class Schema = default: undefined, optional: false, sortable: false, - index: true + index: false }; } else { - value = { type: value, default: undefined, optional: false, sortable: false, index: true }; + value = { type: value, default: undefined, optional: false, sortable: false, index: false }; + if ((value).type === "string" || (value).type === "number") (value).literal = undefined; } data[key] = value; @@ -128,10 +152,9 @@ export class Schema = if (value.type === "array") { if (typeof value.elements === "undefined") value.elements = "string"; - if (typeof value.separator === "undefined") value.separator = ","; + if (typeof value.separator === "undefined") value.separator = "|"; if (typeof value.elements === "object") { value.elements = this.#parse(value.elements).data; - if (!this.options.noLogs) console.log(`'${key}' will not be indexed because array of objects is not yet supported on RediSearch`); } value = this.#fill(value); } else if (value.type === "date") { @@ -141,15 +164,15 @@ export class Schema = } else if (value.type === "object") { if (typeof value.default === "undefined") value.default = undefined; if (typeof value.optional === "undefined") value.optional = false; - if (!value.properties) value.properties = undefined; + if (typeof value.properties === "undefined") value.properties = null; + else if (value.properties instanceof Schema) value.properties = value.properties[schemaData].data; else value.properties = this.#parse(value.properties).data; } else if (value.type === "tuple") { if (typeof value.elements === "undefined") throw new PrettyError("Tuple needs to have at least 1 element", { reference: "redis-om" }); for (let j = 0, le = value.elements.length; j < le; j++) { - //@ts-expect-error No comment - value.elements[j] = this.#parse({ [j]: typeof value.elements[j] === "string" ? value.elements[j] : { type: "object", properties: value.elements[j] } }).data[j]; + value.elements[j] = this.#parse({ [j]: value.elements[j] }).data[j]; } value = this.#fill(value); } else if (value.type === "reference") { @@ -206,6 +229,10 @@ export class Schema = if (typeof value.epsilon === "undefined") value.epsilon = undefined; } value = this.#fill(value); + } else if (value.type === "string" || value.type === "number" || value.type === "bigint") { + if (typeof value.literal === "undefined") value.literal = undefined; + else if (!Array.isArray(value.literal)) value.literal = [value.literal]; + value = this.#fill(value); } else { value = this.#fill(value); } @@ -219,7 +246,7 @@ export class Schema = if (typeof value.default === "undefined") value.default = undefined; if (typeof value.optional === "undefined") value.optional = false; if (typeof value.sortable === "undefined") value.sortable = false; - if (typeof value.index === "undefined") value.index = true; + if (typeof value.index === "undefined") value.index = false; return value; } } \ No newline at end of file diff --git a/src/search/search-builders/base.ts b/src/search/search-builders/base.ts index 4c6aff6..5150660 100644 --- a/src/search/search-builders/base.ts +++ b/src/search/search-builders/base.ts @@ -1,22 +1,22 @@ import type { ParseSchema } from "../../typings"; import type { Search } from "../search"; -export abstract class SearchField> { +export abstract class SearchField, L = unknown> { protected negated: boolean = false; protected value: unknown; - public or: Array = []; + public or: Array = []; public constructor(protected search: Search, protected field: string) { } // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents - public abstract eq(value: Array | unknown): Search; + public abstract eq(value: Array | L): Search; /** Syntactic sugar, calls `eq` */ - public abstract equals(value: unknown): Search; + public abstract equals(value: L): Search; /** Syntactic sugar, calls `eq` */ - public abstract equalsTo(value: unknown): Search; + public abstract equalsTo(value: L): Search; /** Syntactic sugar, return self */ public get does(): this { diff --git a/src/search/search-builders/bigint.ts b/src/search/search-builders/bigint.ts new file mode 100644 index 0000000..4796185 --- /dev/null +++ b/src/search/search-builders/bigint.ts @@ -0,0 +1,45 @@ +import { SearchField } from "./base"; + +import type { ParseSchema } from "../../typings"; +import type { Search } from "../search"; + +export class BigIntField, L extends bigint> extends SearchField { + + public eq(...value: Array): Search; + public eq(value: Array): Search; + public eq(value: Array | L): Search { + return this.#handleMultipleFields(Array.isArray(value) ? value : arguments); + } + + public equals(...value: Array): Search; + public equals(value: Array): Search; + public equals(): Search { + return this.eq(...arguments); + } + + public equalsTo(...value: Array): Search; + public equalsTo(value: Array): Search; + public equalsTo(): Search { + return this.eq(...arguments); + } + + protected construct(): string { + return `{${this.value}${this.or.length > 0 ? ` | ${this.or.join(" | ")}` : ""}}`; + } + + /** @internal */ + #handleMultipleFields(value: Array | IArguments): Search { + const length = value.length; + + this.value = value[0].toString(); + + if (length > 1) { + for (let i = 1; i < length; i++) { + this.or.push(value[i].toString()); + } + } + + this.search._query.push(this); + return this.search; + } +} \ No newline at end of file diff --git a/src/search/search-builders/index.ts b/src/search/search-builders/index.ts index 466969f..b23a7fb 100644 --- a/src/search/search-builders/index.ts +++ b/src/search/search-builders/index.ts @@ -1,8 +1,9 @@ export * from "./base"; +export * from "./boolean"; export * from "./string"; export * from "./number"; -export * from "./boolean"; -export * from "./text"; -export * from "./date"; +export * from "./bigint"; +export * from "./vector"; export * from "./point"; -export * from "./vector"; \ No newline at end of file +export * from "./text"; +export * from "./date"; \ No newline at end of file diff --git a/src/search/search-builders/number.ts b/src/search/search-builders/number.ts index 6b20297..fa74d68 100644 --- a/src/search/search-builders/number.ts +++ b/src/search/search-builders/number.ts @@ -3,67 +3,67 @@ import { SearchField } from "./base"; import type { ParseSchema } from "../../typings"; import type { Search } from "../search"; -export class NumberField> extends SearchField { +export class NumberField, L extends number> extends SearchField { declare protected value: [string, string]; - public eq(value: number): Search { + public eq(value: L): Search { this.value = [value.toString(), value.toString()]; this.search._query.push(this); return this.search; } - public gt(value: number): Search { + public gt(value: L): Search { this.value = [`(${value}`, "+inf"]; this.search._query.push(this); return this.search; } - public gte(value: number): Search { + public gte(value: L): Search { this.value = [value.toString(), "+inf"]; this.search._query.push(this); return this.search; } - public lt(value: number): Search { + public lt(value: L): Search { this.value = ["-inf", `(${value}`]; this.search._query.push(this); return this.search; } - public lte(value: number): Search { + public lte(value: L): Search { this.value = ["-inf", value.toString()]; this.search._query.push(this); return this.search; } - public between(lower: number, upper: number): Search { + public between(lower: L, upper: L): Search { this.value = [lower.toString(), upper.toString()]; this.search._query.push(this); return this.search; } - public equals(value: number): Search { + public equals(value: L): Search { return this.eq(value); } - public equalsTo(value: number): Search { + public equalsTo(value: L): Search { return this.eq(value); } - public greaterThan(value: number): Search { + public greaterThan(value: L): Search { return this.gt(value); } - public greaterThanOrEqualTo(value: number): Search { + public greaterThanOrEqualTo(value: L): Search { return this.gte(value); } - public lessThan(value: number): Search { + public lessThan(value: L): Search { return this.lt(value); } - public lessThanOrEqualTo(value: number): Search { + public lessThanOrEqualTo(value: L): Search { return this.lte(value); } diff --git a/src/search/search-builders/string.ts b/src/search/search-builders/string.ts index 4559f0a..2a87eb3 100644 --- a/src/search/search-builders/string.ts +++ b/src/search/search-builders/string.ts @@ -3,22 +3,22 @@ import { SearchField } from "./base"; import type { ParseSchema } from "../../typings"; import type { Search } from "../search"; -export class StringField> extends SearchField { +export class StringField, L extends string> extends SearchField { - public eq(...value: Array): Search; - public eq(value: Array): Search; - public eq(value: Array | string): Search { + public eq(...value: Array): Search; + public eq(value: Array): Search; + public eq(value: Array | L): Search { return this.#handleMultipleFields(Array.isArray(value) ? value : arguments); } - public equals(...value: Array): Search; - public equals(value: Array): Search; + public equals(...value: Array): Search; + public equals(value: Array): Search; public equals(): Search { return this.eq(...arguments); } - public equalsTo(...value: Array): Search; - public equalsTo(value: Array): Search; + public equalsTo(...value: Array): Search; + public equalsTo(value: Array): Search; public equalsTo(): Search { return this.eq(...arguments); } diff --git a/src/search/search-builders/text.ts b/src/search/search-builders/text.ts index ca701f2..4f3a175 100644 --- a/src/search/search-builders/text.ts +++ b/src/search/search-builders/text.ts @@ -74,7 +74,7 @@ export class TextField> extends SearchField { } /** @internal */ - #handleMultipleFields(value: Array | IArguments, exact: boolean = false): Search { + #handleMultipleFields(value: Array | IArguments, exact: boolean = false): Search { const length = value.length; this.value = { val: value[0], exact }; diff --git a/src/search/search.ts b/src/search/search.ts index dd51767..e018611 100644 --- a/src/search/search.ts +++ b/src/search/search.ts @@ -6,25 +6,26 @@ import type { SearchOptions, SearchReply } from "redis"; import { type SearchField, + BooleanField, StringField, NumberField, - BooleanField, - TextField, - DateField, + BigIntField, + VectorField, PointField, - VectorField + TextField, + DateField } from "./search-builders"; import type { - FieldTypes, + ParseSearchSchema, + SearchInformation, NodeRedisClient, MapSearchField, + ReturnDocument, ParseSchema, - ParseSearchSchema, BaseField, ParsedMap, - ReturnDocument, - SearchInformation + FieldType } from "../typings"; export type SearchReturn>> = Omit; @@ -37,7 +38,7 @@ export class Search, P extends ParseSearchSchema, P extends ParseSearchSchema(field: F, order: "ASC" | "DESC" = "ASC"): SearchSortReturn> { this.#options.SORTBY = { BY: field, DIRECTION: order }; return this; @@ -112,9 +109,11 @@ export class Search, P extends ParseSearchSchema(offset: number, count: number, autoFetch?: F): Promise>> { + public async page(offset: number, count: number, autoFetch?: F): Promise> | undefined> { + const { total, documents } = await this.#search({ LIMIT: { from: offset, size: count } }); + if (total === 0) return void 0; + const docs = []; - const { documents } = await this.#search({ LIMIT: { from: offset, size: count } }); for (let i = 0, len = documents.length; i < len; i++) { const doc = documents[i]; @@ -132,7 +131,7 @@ export class Search, P extends ParseSearchSchemathis.#schema, { globalPrefix: this.#information.globalPrefix, prefix: this.#information.prefix, name: this.#information.modelName @@ -142,9 +141,11 @@ export class Search, P extends ParseSearchSchemadocs; } - public async pageOfIds(offset: number, count: number, idOnly: boolean = false): Promise> { + public async pageOfIds(offset: number, count: number, idOnly: boolean = false): Promise | undefined> { + const { total, documents } = await this.#search({ LIMIT: { from: offset, size: count } }, true); + if (total === 0) return void 0; + const docs: Array = []; - const { documents } = await this.#search({ LIMIT: { from: offset, size: count } }, true); for (let j = 0, len = documents.length; j < len; j++) { const doc = documents[j]; @@ -154,33 +155,36 @@ export class Search, P extends ParseSearchSchema(autoFetch?: F): Promise> { - return (await this.page(0, 1, autoFetch))[0]; + public async first(autoFetch?: F): Promise | undefined> { + return (await this.page(0, 1, autoFetch))?.[0]; } - public async firstId(withKey: boolean = false): Promise { - return (await this.pageOfIds(0, 1, withKey))[0]; + public async firstId(withKey: boolean = false): Promise { + return (await this.pageOfIds(0, 1, withKey))?.[0]; } - public async min(field: F, autoFetch?: AF): Promise> { + public async min(field: F, autoFetch?: AF): Promise | undefined> { return await this.sortBy(field, "ASC").first(autoFetch); } - public async minId(field: F): Promise { + public async minId(field: F): Promise { return await this.sortBy(field, "ASC").firstId(); } - public async max(field: F, autoFetch?: AF): Promise> { + public async max(field: F, autoFetch?: AF): Promise | undefined> { return await this.sortBy(field, "DESC").first(autoFetch); } - public async maxId(field: F): Promise { + public async maxId(field: F): Promise { return await this.sortBy(field, "DESC").firstId(); } - public async all(autoFetch?: F): Promise>> { + public async all(autoFetch?: F): Promise> | undefined> { + const { total } = await this.#search({ LIMIT: { from: 0, size: 0 } }); + if (total === 0) return void 0; + + const { documents } = await this.#search({ LIMIT: { from: 0, size: total } }); const docs = []; - const { documents } = await this.#search({ LIMIT: { from: 0, size: (await this.#search({ LIMIT: { from: 0, size: 0 } })).total } }); for (let i = 0, len = documents.length; i < len; i++) { const doc = documents[i]; @@ -198,7 +202,7 @@ export class Search, P extends ParseSearchSchemaawait Promise.all(temp); } } - docs.push(new this.#docType(this.#schema, { + docs.push(new this.#docType(this.#schema, { globalPrefix: this.#information.globalPrefix, prefix: this.#information.prefix, name: this.#information.modelName @@ -208,10 +212,12 @@ export class Search, P extends ParseSearchSchemadocs; } - public async allIds(idOnly: boolean = false): Promise> { - const docs: Array = []; + public async allIds(idOnly: boolean = false): Promise | undefined> { + const { total } = await this.#search({ LIMIT: { from: 0, size: 0 } }); + if (total === 0) return void 0; - const { documents } = await this.#search({ LIMIT: { from: 0, size: (await this.#search({ LIMIT: { from: 0, size: 0 } })).total } }); + const { documents } = await this.#search({ LIMIT: { from: 0, size: total } }); + const docs: Array = []; for (let i = 0, len = documents.length; i < len; i++) { const doc = documents[i]; @@ -225,43 +231,43 @@ export class Search, P extends ParseSearchSchema(autoFetch?: F): Promise>> { + public async returnAll(autoFetch?: F): Promise> | undefined> { return await this.all(autoFetch); } - public async returnAllIds(withKey: boolean = false): Promise> { + public async returnAllIds(withKey: boolean = false): Promise | undefined> { return await this.allIds(withKey); } - public async returnPage(offset: number, count: number, autoFetch?: F): Promise>> { - return await this.page(offset, count, autoFetch); + public async returnPage(offset: number, count: number, autoFetch?: F): Promise> | undefined> { + return await this.page(offset, count, autoFetch); } - public async returnPageOfIds(offset: number, count: number, withKey: boolean = false): Promise> { + public async returnPageOfIds(offset: number, count: number, withKey: boolean = false): Promise | undefined> { return await this.pageOfIds(offset, count, withKey); } - public async returnFirst(autoFetch?: F): Promise> { + public async returnFirst(autoFetch?: F): Promise | undefined> { return await this.first(autoFetch); } - public async returnFirstId(withKey: boolean = false): Promise { + public async returnFirstId(withKey: boolean = false): Promise { return await this.firstId(withKey); } - public async returnMin(field: F, autoFetch?: AF): Promise> { + public async returnMin(field: F, autoFetch?: AF): Promise | undefined> { return await this.min(field, autoFetch); } - public async returnMinId(field: F): Promise { + public async returnMinId(field: F): Promise { return await this.minId(field); } - public async returnMax(field: F, autoFetch?: AF): Promise> { + public async returnMax(field: F, autoFetch?: AF): Promise | undefined> { return await this.max(field, autoFetch); } - public async returnMaxId(field: F): Promise { + public async returnMaxId(field: F): Promise { return await this.maxId(field); } @@ -277,7 +283,7 @@ export class Search, P extends ParseSearchSchemanew this.#docType(this.#schema, { + return new this.#docType(this.#schema, { globalPrefix: this.#information.globalPrefix, prefix: this.#information.prefix, name: this.#information.modelName @@ -319,20 +325,24 @@ export class Search, P extends ParseSearchSchemathis.#defineReturn(path, value.type === "array" ? value.elements : value.type); + return this.#defineReturn(searchPath, type); } - #defineReturn(field: string, type: Exclude): BaseField { + #defineReturn(field: string, type: Exclude): BaseField { switch (type) { case "string": { this.#workingType = "string"; - return new StringField(this, field); + return new StringField(this, field); } case "number": { this.#workingType = "number"; - return new NumberField(this, field); + return new NumberField(this, field); + } + case "bigint": { + this.#workingType = "bigint"; + return new BigIntField(this, field); } case "boolean": { this.#workingType = "boolean"; diff --git a/src/typings/doc-base.ts b/src/typings/doc-base.ts index 2c55913..94d7508 100644 --- a/src/typings/doc-base.ts +++ b/src/typings/doc-base.ts @@ -1,3 +1,3 @@ export interface DocumentShared { - toString: () => string; + toString: () => string | Array; } \ No newline at end of file diff --git a/src/typings/field-map.ts b/src/typings/field-map.ts index 090e582..fec4666 100644 --- a/src/typings/field-map.ts +++ b/src/typings/field-map.ts @@ -7,6 +7,7 @@ import type { Point } from "./point"; export interface FieldMap { string: string; number: number; + bigint: bigint; boolean: boolean; text: string; date: Date | number; diff --git a/src/typings/index.ts b/src/typings/index.ts index 68bc9e9..fb7368b 100644 --- a/src/typings/index.ts +++ b/src/typings/index.ts @@ -15,4 +15,5 @@ export type * from "./field-map"; export type * from "./doc-base"; export type * from "./modules"; export type * from "./client"; +export type * from "./utils"; export type * from "./point"; \ No newline at end of file diff --git a/src/typings/map-schema.ts b/src/typings/map-schema.ts index 41e0095..0e312d2 100644 --- a/src/typings/map-schema.ts +++ b/src/typings/map-schema.ts @@ -1,29 +1,50 @@ import type { ParseSchema } from "./parse-schema"; import type { ReferenceArray } from "../utils"; import type { FieldMap } from "./field-map"; +import type { Expand } from "./utils"; export type MapSchema< T extends ParseSchema, AF extends boolean = false, CAS extends boolean = false -> = MapSchemaData & MapSchemaReferences; +> = Expand & MapSchemaReferences>; type MapSchemaData["data"], CAS extends boolean = false> = { - [K in keyof T as T[K]["type"] extends "object" ? K : T[K]["optional"] extends true ? never : T[K]["default"] extends {} ? CAS extends true ? never : K : never]: _MapSchemaData + [K in keyof T as T[K]["optional"] extends false + ? T[K]["default"] extends {} + ? CAS extends true + ? never + : K + : K + : T[K]["default"] extends {} + ? CAS extends true + ? never + : K + : never]: _MapSchemaData } & { - [K in keyof T as T[K]["type"] extends "object" ? never : T[K]["optional"] extends true ? K : T[K]["default"] extends {} ? CAS extends true ? K : never : K]?: _MapSchemaData + [K in keyof T as T[K]["optional"] extends false + ? T[K]["default"] extends {} + ? CAS extends true + ? K + : never + : never + : T[K]["default"] extends {} + ? CAS extends true + ? K + : never + : K]?: _MapSchemaData }; // eslint-disable-next-line @typescript-eslint/naming-convention type _MapSchemaData["data"][number]> = T extends { properties: unknown } ? T["properties"] extends ParseSchema - ? MapSchema + ? Expand> : T["properties"] extends ParseSchema["data"] - ? MapSchemaData + ? Expand> : unknown : T extends { elements: unknown } ? T["elements"] extends [unknown, ...Array] - ? Test + ? ParseTupleElements : T["elements"] extends object ? Array> : FieldMap["array"] @@ -33,6 +54,12 @@ type _MapSchemaData["data"][number]> = T extends { pr : T extends { vecType: "FLOAT64" } ? Float64Array | Array : unknown + : T extends { literal: unknown } + ? T["literal"] extends {} + ? T["literal"] extends Array + ? T["literal"][number] + : T["literal"] + : FieldMap[T["type"]] : FieldMap[T["type"]] ; @@ -47,6 +74,6 @@ type _MapSchemaReferences["references"][number], AF e ? Array> : ReferenceArray; -type Test = { +type ParseTupleElements = { [K in keyof T]: T[K] extends ParseSchema["data"][number] ? _MapSchemaData : never }; \ No newline at end of file diff --git a/src/typings/map-search-fields.ts b/src/typings/map-search-fields.ts index 9381eab..a88b36a 100644 --- a/src/typings/map-search-fields.ts +++ b/src/typings/map-search-fields.ts @@ -1,27 +1,30 @@ import type { ParseSchema } from "./parse-schema"; import type { BooleanField, - DateField, + StringField, NumberField, + BigIntField, + VectorField, PointField, - StringField, TextField, - VectorField + DateField } from "../search/search-builders"; -export type MapSearchField, T extends ParseSearchSchema> = T[K] extends "string" - ? StringField - : T[K] extends "number" - ? NumberField - : T[K] extends "boolean" +export type MapSearchField, T extends ParseSearchSchema> = T[K][0] extends "string" + ? StringField + : T[K][0] extends "number" + ? NumberField + : T[K][0] extends "bigint" + ? BigIntField + : T[K][0] extends "boolean" ? BooleanField - : T[K] extends "text" + : T[K][0] extends "text" ? TextField - : T[K] extends "date" + : T[K][0] extends "date" ? DateField - : T[K] extends "point" + : T[K][0] extends "point" ? PointField - : T[K] extends "vector" + : T[K][0] extends "vector" ? VectorField : never; @@ -47,11 +50,17 @@ export type GetFinalProperty["data" : S[Head] extends { elements: unknown } ? GetFinalProperty : never - : S[T] extends { elements: unknown } - ? S[T]["elements"] extends {} - ? S[T]["elements"] - : "string" - : S[T]["type"]; + : [S[T] extends { elements: unknown } + ? S[T]["elements"] extends {} + ? S[T]["elements"] + : "string" + : S[T]["type"], + S[T] extends { literal: unknown } + ? S[T]["literal"] extends Array + ? S[T]["literal"][number] + : S[T]["literal"] + : never + ]; export type ParseSearchSchema["data"]> = { [K in SchemaToStrings]: GetFinalProperty diff --git a/src/typings/parse-schema.ts b/src/typings/parse-schema.ts index 8b904b2..f0456af 100644 --- a/src/typings/parse-schema.ts +++ b/src/typings/parse-schema.ts @@ -1,14 +1,20 @@ import type { ExtractParsedSchemaDefinition } from "./extract-generic"; +import type { Schema } from "../schema"; + import type { + SchemaDefinition, + ReferenceField, + ObjectField, + StringField, + NumberField, + VectorField, ArrayField, - BaseField, + TupleField, FlatVector, HNSWVector, - ObjectField, - ReferenceField, - SchemaDefinition, - TupleField, - VectorField + BaseField, + FieldType, + BigIntField } from "./schema-definition"; export type ParseSchema = { @@ -16,7 +22,13 @@ export type ParseSchema = { [K in keyof T as T[K] extends ReferenceField ? never : K]: T[K] extends ObjectField ? { [P in keyof Required]: P extends "properties" - ? T[K][P] extends {} ? ParseSchema["data"] : undefined + ? T[K][P] extends {} + ? T[K][P] extends Schema + ? U + : T[K][P] extends SchemaDefinition + ? ParseSchema["data"] + : never + : undefined : T[K][P] extends {} ? T[K][P] : Fill

} : T[K] extends ArrayField @@ -33,8 +45,8 @@ export type ParseSchema = { ? { [U in keyof V]: V[U] extends string ? CreateDefinitionFromString - : V[U] extends SchemaDefinition - ? ParseSchema<{ $: { type: "object", properties: V[U] } }>["data"]["$"] + : V[U] extends FieldType + ? GetTupleObject : never } : never @@ -50,6 +62,18 @@ export type ParseSchema = { [P in keyof Required]: T[K][P] extends {} ? T[K][P] : Fill

} : never + : T[K] extends StringField + ? { + [P in keyof Required]: T[K][P] extends {} ? T[K][P] : Fill

+ } + : T[K] extends NumberField + ? { + [P in keyof Required]: T[K][P] extends {} ? T[K][P] : Fill

+ } + : T[K] extends BigIntField + ? { + [P in keyof Required]: T[K][P] extends {} ? T[K][P] : Fill

+ } : T[K] extends BaseField ? { [P in keyof Required]: T[K][P] extends {} ? T[K][P] : Fill

@@ -83,6 +107,24 @@ export type CreateDefinitionFromString = T extends "vector" ? "L2" : Fill } + : T extends "string" + ? { + [K in keyof Required]: K extends "type" + ? T + : Fill + } + : T extends "number" + ? { + [K in keyof Required]: K extends "type" + ? T + : Fill + } + : T extends "bigint" + ? { + [K in keyof Required]: K extends "type" + ? T + : Fill + } : { [K in keyof Required]: K extends "type" ? T @@ -97,5 +139,7 @@ export type Fill = T extends "optional" : T extends "sortable" ? false : T extends "index" - ? true - : undefined; \ No newline at end of file + ? false + : undefined; + +type GetTupleObject["data"]> = P extends { $: unknown } ? P["$"] : never; \ No newline at end of file diff --git a/src/typings/parsed-search-schema.ts b/src/typings/parsed-search-schema.ts index 6f6641a..67f98c4 100644 --- a/src/typings/parsed-search-schema.ts +++ b/src/typings/parsed-search-schema.ts @@ -1,8 +1,11 @@ -import type { FieldTypes, ObjectField, ReferenceField, TupleField } from "./schema-definition"; +import type { FieldMap } from "./field-map"; -export interface Parsed { - value: Exclude; - path: string; +export interface ParsedSchemaToSearch { + map: ParsedMap; + index: Array; } -export type ParsedMap = Map; \ No newline at end of file +export type ParsedMap = Map, + searchPath: string +}>; \ No newline at end of file diff --git a/src/typings/return-doc.ts b/src/typings/return-doc.ts index 9484c41..92cd9d1 100644 --- a/src/typings/return-doc.ts +++ b/src/typings/return-doc.ts @@ -3,8 +3,8 @@ import type { ParseSchema } from "./parse-schema"; import type { MapSchema } from "./map-schema"; import type { Schema } from "../schema"; -export type Doc = JSONDocument | HASHDocument; +export type Document = JSONDocument | HASHDocument; export type ReturnDocument | ParseSchema, AF extends boolean = false> = T extends Schema - ? Doc & MapSchema - : Doc & MapSchema, AF>; \ No newline at end of file + ? Document & MapSchema + : T extends ParseSchema ? Document & MapSchema : never; \ No newline at end of file diff --git a/src/typings/schema-definition.ts b/src/typings/schema-definition.ts index 27e1f84..318fdba 100644 --- a/src/typings/schema-definition.ts +++ b/src/typings/schema-definition.ts @@ -2,16 +2,33 @@ import type { FieldMap } from "./field-map"; import type { Schema } from "../schema"; import type { Point } from "./point"; -export type SchemaDefinition = Record | FieldTypes>; +export type SchemaDefinition = Record | FieldType>; -export type FieldTypes = StringField | NumberField | BooleanField | TextField | DateField | PointField | ArrayField | TupleField | ObjectField | ReferenceField | VectorField; +export interface ParsedSchemaDefinition { + data: Record; + references: Record; +} + +export type FieldType = StringField | NumberField | BigIntField | BooleanField | TextField | DateField | PointField | ArrayField | TupleField | ObjectField | ReferenceField | VectorField; + +export type ParsedFieldType = ParsedStringField + | ParsedNumberField + | ParsedBigIntField + | ParsedObjectField + | ParsedArrayField + | ParsedTupleField + | Required + | Required + | Required + | Required + | Required; -export type TupleElement = Exclude | SchemaDefinition | undefined; +export type TupleElement = Exclude | FieldType; export interface BaseField { type: keyof FieldMap; - optional?: boolean | undefined; default?: FieldMap[keyof FieldMap] | undefined; + optional?: boolean; sortable?: boolean; index?: boolean; } @@ -20,12 +37,34 @@ export interface BaseField { export interface StringField extends BaseField { type: "string"; default?: string | undefined; + literal?: string | Array | undefined; + caseSensitive?: boolean | undefined; +} + +export interface ParsedStringField extends Required { + literal: Array | undefined; } // NUMERIC export interface NumberField extends BaseField { type: "number"; default?: number | undefined; + literal?: number | Array | undefined; +} + +export interface ParsedNumberField extends Required { + literal: Array | undefined; +} + +// TAG +export interface BigIntField extends BaseField { + type: "bigint"; + default?: bigint | undefined; + literal?: bigint | Array | undefined; +} + +export interface ParsedBigIntField extends Required { + literal: Array | undefined; } // TAG @@ -38,6 +77,8 @@ export interface BooleanField extends BaseField { export interface TextField extends BaseField { type: "text"; default?: string | undefined; + phonetic?: "dm:en" | "dm:fr" | "dm:pt" | "dm:es"; + weight?: number | undefined; } // NUMERIC @@ -86,6 +127,15 @@ export interface ArrayField extends BaseField { separator?: string; } +export interface ParsedArrayField extends Required { + type: "array"; + elements: Exclude | ParsedSchemaDefinition["data"]; + default: Array | undefined; + + /** Default: `|` */ + separator: string; +} + //FALLBACK export interface TupleField extends Omit { type: "tuple"; @@ -93,13 +143,25 @@ export interface TupleField extends Omit { default?: Array | undefined; } +export interface ParsedTupleField extends Omit, "sortable"> { + type: "tuple"; + elements: [ParsedFieldType, ...Array]; + default: Array | undefined; +} + // FALLBACK export interface ObjectField extends Omit { type: "object"; - properties?: SchemaDefinition | undefined; + properties?: Schema | SchemaDefinition | undefined; default?: Record | undefined; } +export interface ParsedObjectField extends Omit, "sortable"> { + type: "object"; + properties: Record | null; + default: Record | undefined; +} + // NON EXISTENT HANDLE AS ARRAY OF STRINGS WITH AUTOFETCH TRANSFORMING INTO AN OBJECT export interface ReferenceField extends Pick { type: "reference"; diff --git a/src/typings/schema-options.ts b/src/typings/schema-options.ts index 2acc874..b694137 100644 --- a/src/typings/schema-options.ts +++ b/src/typings/schema-options.ts @@ -1,7 +1,33 @@ export interface SchemaOptions { dataStructure?: "HASH" | "JSON" | undefined; + + /** For word Stemming */ + language?: Language; + stopWords?: Array; skipDocumentValidation?: boolean | undefined; noLogs?: boolean | undefined; prefix?: string | undefined; suffix?: string | (() => string) | undefined; -} \ No newline at end of file +} + +export type Language = "arabic" + | "armenian" + | "danish" + | "dutch" + | "english" + | "finnish" + | "french" + | "german" + | "hungarian" + | "italian" + | "norwegian" + | "portuguese" + | "romanian" + | "russian" + | "serbian" + | "spanish" + | "swedish" + | "tamil" + | "turkish" + | "yiddish" + | "chinese"; \ No newline at end of file diff --git a/src/typings/utils.ts b/src/typings/utils.ts new file mode 100644 index 0000000..d19893e --- /dev/null +++ b/src/typings/utils.ts @@ -0,0 +1,15 @@ +export type Narrow = + | _Narrow + | _Narrow + | _Narrow + | _Narrow + | _Narrow + | _Narrow + | _Narrow + | (T extends object ? { [K in keyof T]: Narrow } : never) + | Extract<{} | null | undefined, T>; + +// eslint-disable-next-line @typescript-eslint/naming-convention +type _Narrow = [U] extends [T] ? U : Extract; + +export type Expand = T extends T ? { [K in keyof T]: T[K] } : never; \ No newline at end of file diff --git a/src/utils/index.ts b/src/utils/index.ts index b53bd5b..380340c 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,2 +1,2 @@ export * from "./reference-array"; -export * from "./parse"; \ No newline at end of file +export * from "./parse-to-search-index"; \ No newline at end of file diff --git a/src/utils/parse-to-search-index.ts b/src/utils/parse-to-search-index.ts new file mode 100644 index 0000000..062db0c --- /dev/null +++ b/src/utils/parse-to-search-index.ts @@ -0,0 +1,159 @@ +import type { + ParsedSchemaDefinition, + ParsedSchemaToSearch, + ParsedArrayField, + ParsedFieldType, + VectorField, + ParsedMap +} from "../typings"; + +export function parseSchemaToSearchIndex( + schema: ParsedSchemaDefinition["data"], + structure: "JSON" | "HASH", + { previousKey, previousPath, arrayKey }: { previousKey?: string, previousPath?: string, arrayKey?: string } = {} +): ParsedSchemaToSearch { + let objs: ParsedMap = new Map(); + let index: Array = []; + + for (let i = 0, entries = Object.entries(schema), len = entries.length; i < len; i++) { + const [key, value] = entries[i]; + const withPreviousKey = previousKey ? `${previousKey}.${key}` : key; + const withPreviousPath = previousPath ? `${previousPath}_${key}` : key; + + if (value.type === "object") { + if (value.properties === null) continue; + const parsed = parseSchemaToSearchIndex( + value.properties, + structure, + { + previousKey: withPreviousKey, + previousPath: withPreviousPath + } + ); + + objs = new Map([...objs, ...parsed.map]); + continue; + } + + if (!value.index) continue; + + if (value.type === "array") { + if (typeof value.elements === "object") { + const parsed = parseSchemaToSearchIndex( + value.elements, + structure, + { + previousKey: withPreviousKey, + previousPath: withPreviousPath, + arrayKey: previousKey ? `${previousKey}.${key}${getArrayModifier(value.elements)}` : `${key}${getArrayModifier(value.elements)}` + } + ); + objs = new Map([...objs, ...parsed.map]); + continue; + } + + arrayKey = `${arrayKey ? `${arrayKey}.${key}` : withPreviousKey}${getArrayModifier(value.elements)}`; + } + + if (value.type === "tuple") { + for (let j = 0, length = value.elements.length; j < length; j++) { + const indexValue = value.elements[j]; + + const parsed = parseSchemaToSearchIndex( + { [j.toString()]: indexValue }, + structure, + { + previousKey: withPreviousKey, + previousPath: withPreviousPath + } + ); + + objs = new Map([...objs, ...parsed.map]); + + } + continue; + } + + const actualType = value.type === "array" + // We are handling it already so we can cast + ? >value.elements + : value.type; + + objs.set(withPreviousKey, { + type: actualType, + searchPath: withPreviousPath + }); + + const prefix = structure === "JSON" ? "$." : ""; + + index.push( + `${prefix}${arrayKey ? arrayKey : withPreviousKey}`, + "AS", + withPreviousPath, + getSearchType(actualType) + ); + + if (value.type === "vector") { + index.push( + value.algorithm, + getCount(value), + "TYPE", + value.vecType, + "DIM", + value.dim.toString(), + "DISTANCE_METRIC", + value.distance + ); + + if (value.cap) index.push("INITIAL_CAP", value.cap.toString()); + + if (value.algorithm === "FLAT") { + if (value.size) index.push("BLOCK_SIZE", value.size.toString()); + } else { + if (value.m) index.push("M", value.m.toString()); + if (value.construction) index.push("EF_CONSTRUCTION", value.construction.toString()); + if (value.runtime) index.push("EF_RUNTIME", value.runtime.toString()); + if (value.epsilon) index.push("EPSILON", value.epsilon.toString()); + } + } + + if (value.sortable) index.push("SORTABLE"); + if (value.type === "string" && value.caseSensitive) index.push("CASESENSITIVE"); + if (value.type === "text") { + if (typeof value.phonetic !== "undefined") index.push("PHONETIC", value.phonetic); + if (typeof value.weight !== "undefined") index.push("WEIGTH", value.weight.toString()); + } + } + + return { map: objs, index }; +} + +function getArrayModifier(elements: ParsedArrayField["elements"]): "*" | "[*]" | "" { + if (typeof elements === "object" || elements === "vector") return "[*]"; + if (elements === "string" || elements === "boolean") return "*"; + return ""; +} + +function getSearchType(type: ParsedFieldType["type"]): "TAG" | "NUMERIC" | "TEXT" | "GEO" | "VECTOR" { + if (type === "text") return "TEXT"; + if (type === "number" || type === "date") return "NUMERIC"; + if (type === "point") return "GEO"; + if (type === "vector") return "VECTOR"; + return "TAG"; +} + +function getCount(value: VectorField): string { + let count = 6; + + if (value.cap) count += 2; + if (value.algorithm === "FLAT") { + if (value.size) count += 2; + } else { + if (value.m) count += 2; + if (value.construction) count += 2; + if (value.runtime) count += 2; + if (value.epsilon) count += 2; + } + + return count.toString(); +} \ No newline at end of file diff --git a/src/utils/parse.ts b/src/utils/parse.ts deleted file mode 100644 index bbda2fc..0000000 --- a/src/utils/parse.ts +++ /dev/null @@ -1,39 +0,0 @@ -import type { ParsedMap, ParseSchema } from "../typings"; - -export function parseSchemaToSearchIndex(schema: ParseSchema["data"], k?: string, p?: string): ParsedMap { - let objs: ParsedMap = new Map(); - - for (let i = 0, entries = Object.entries(schema), len = entries.length; i < len; i++) { - const [key, value] = entries[i]; - - if (value.type === "object") { - //@ts-expect-error Typescript is getting confused due to the union of array and object - if (typeof value.properties === "undefined") continue; - //@ts-expect-error Typescript is getting confused due to the union of array and object - const parsed = parseSchemaToSearchIndex(value.properties, k ? `${k}.${key}` : key, p ? `${k}_${key}` : key); - objs = new Map([...objs, ...parsed]); - continue; - } - - if (!value.index) continue; - - //@ts-expect-error Typescript is getting confused due to the union of array and object - if (value.type === "array" && typeof value.elements === "object") continue; - if (value.type === "tuple") { - //@ts-expect-error Typescript is getting confused due to the union of array and object - for (let j = 0, le = value.elements.length; j < le; j++) { - //@ts-expect-error Typescript is getting confused due to the union of array and object - const temp = value.elements[j]; - - const parsed = parseSchemaToSearchIndex({ [j]: temp }, k ? `${k}.${key}` : key, p ? `${k}_${key}` : key); - objs = new Map([...objs, ...parsed]); - - } - continue; - } - //@ts-expect-error ParseSchema type is confusing this (not much i can do rly) - objs.set(k ? `${k}.${key}` : key, { value: value, path: p ? `${p}_${key}` : key }); - } - - return objs; -} \ No newline at end of file diff --git a/src/utils/reference-array.ts b/src/utils/reference-array.ts index 66febef..75956ef 100644 --- a/src/utils/reference-array.ts +++ b/src/utils/reference-array.ts @@ -1,9 +1,9 @@ import { HASHDocument, JSONDocument } from "../document"; -import type { Doc } from "../typings"; +import type { Document } from "../typings"; export class ReferenceArray extends Array { - public reference(...recordOrDoc: Array): this { + public reference(...recordOrDoc: Array): this { for (let i = 0, len = recordOrDoc.length; i < len; i++) { const tempVal = recordOrDoc[i]; const tempId = tempVal instanceof JSONDocument || tempVal instanceof HASHDocument ? tempVal.$record_id : tempVal; From 394fb1c72f3a9960e8b0b47e7184045ebadf5ea1 Mon Sep 17 00:00:00 2001 From: DidaS Date: Thu, 2 Nov 2023 19:28:54 +0000 Subject: [PATCH 4/4] Merge `nekdis 0.13.4` -> `redis-om` --- .gitignore | 3 +- examples/table-module.ts | 20 +- package.json | 14 +- pnpm-lock.yaml | 767 +++++++++--------- src/client.ts | 57 +- .../document-helpers/general-helpers.ts | 7 +- src/document/document-helpers/hash-helpers.ts | 23 +- src/document/document-helpers/index.ts | 4 +- src/document/document-helpers/json-helpers.ts | 6 +- src/document/hash-document.ts | 26 +- src/document/json-document.ts | 19 +- src/index.ts | 11 +- src/model.ts | 344 +++++--- src/relation/relation.ts | 89 ++ src/schema.ts | 118 ++- src/scripts/create-relation.lua | 185 +++++ src/search/search-builders/vector.ts | 8 +- src/search/search.ts | 45 +- src/typings/class-options.ts | 76 ++ src/typings/client-modules.ts | 12 + src/typings/document-base.ts | 3 + src/typings/extract-schema-generics.ts | 5 + src/typings/field-map.ts | 9 +- src/typings/index.ts | 23 +- src/typings/map-schema-to-object.ts | 87 ++ src/typings/map-schema-to-search-strings.ts | 69 ++ src/typings/methods-definition.ts | 2 +- src/typings/parse-schema.ts | 181 +++-- src/typings/parsed-schema-to-internal.ts | 13 + src/typings/point.ts | 4 +- src/typings/return-document.ts | 16 + src/typings/schema-and-fields-definition.ts | 197 +++++ src/utils/index.ts | 4 +- src/utils/parse-to-search-index.ts | 44 +- src/utils/reference-array.ts | 2 +- 35 files changed, 1783 insertions(+), 710 deletions(-) create mode 100644 src/relation/relation.ts create mode 100644 src/scripts/create-relation.lua create mode 100644 src/typings/class-options.ts create mode 100644 src/typings/client-modules.ts create mode 100644 src/typings/document-base.ts create mode 100644 src/typings/extract-schema-generics.ts create mode 100644 src/typings/map-schema-to-object.ts create mode 100644 src/typings/map-schema-to-search-strings.ts create mode 100644 src/typings/parsed-schema-to-internal.ts create mode 100644 src/typings/return-document.ts create mode 100644 src/typings/schema-and-fields-definition.ts diff --git a/.gitignore b/.gitignore index 9dd2987..08095bc 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ dist/ bench-dist/ coverage/ .DS_Store -.dccache \ No newline at end of file +.dccache +*v8.log \ No newline at end of file diff --git a/examples/table-module.ts b/examples/table-module.ts index 156eb04..c6eefe7 100644 --- a/examples/table-module.ts +++ b/examples/table-module.ts @@ -1,8 +1,6 @@ import { PrettyError } from "@infinite-fansub/logger"; -import { Client, Model } from "../src"; -import { ModelOptions } from "../src/typings"; - +import { Client, Model, ModelOptions } from "../src"; /* This module allows you to define tables to use with normal redis operations However it doesn't affect the `Search` functionality since it doesn't have suffix constrains @@ -41,7 +39,7 @@ class Table> { type TableFunction = ((table: Table) => Table | Promise>) | Table; const client = new Client({ - inject: { + base: { schema: { methods: { withTable: async function (name: string, table: TableFunction) { @@ -79,17 +77,12 @@ const client = new Client({ /* Expected Redis log: - "JSON.GET" "redis-om:V1:test:1" + "JSON.GET" "Nekdis:V1:test:1" Expected result: JSONDocument { - '$global_prefix': 'redis-om', - '$prefix': 'V1', - '$model_name': 'test', - '$suffix': undefined, '$id': '1', - '$record_id': 'redis-om:V1:test:1', name: 'DidaS', age: 18 } @@ -118,17 +111,12 @@ const client = new Client({ /* Expected Redis log: - "JSON.GET" "redis-om:V1:test:table1:1" + "JSON.GET" "Nekdis:V1:test:table1:1" Expected result: JSONDocument { - '$global_prefix': 'redis-om', - '$prefix': 'V1', - '$model_name': 'test', - '$suffix': 'table1', '$id': '1', - '$record_id': 'redis-om:V1:test:table1:1', name: undefined, age: 21 } diff --git a/package.json b/package.json index 5b086ef..83ef85b 100644 --- a/package.json +++ b/package.json @@ -45,21 +45,21 @@ "devDependencies": { "@microsoft/tsdoc": "^0.14.2", "@microsoft/tsdoc-config": "^0.16.2", - "@types/node": "^20.6.0", - "@typescript-eslint/eslint-plugin": "^6.6.0", - "@typescript-eslint/parser": "^6.6.0", + "@types/node": "^20.8.10", + "@typescript-eslint/eslint-plugin": "^6.9.1", + "@typescript-eslint/parser": "^6.9.1", "c8": "^8.0.1", - "eslint": "^8.49.0", - "eslint-plugin-import": "^2.28.1", + "eslint": "^8.52.0", + "eslint-plugin-import": "^2.29.0", "eslint-plugin-tsdoc": "^0.2.17", "ts-node": "^10.9.1", "typescript": "^5.2.2", - "vitest": "^0.34.4" + "vitest": "^0.34.6" }, "dependencies": { "@infinite-fansub/logger": "^2.2.2", "colours.js": "^3.1.2", - "redis": "^4.6.8", + "redis": "^4.6.10", "tslib": "^2.6.2" } } \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7793080..fd83a73 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,8 +12,8 @@ dependencies: specifier: ^3.1.2 version: 3.1.2 redis: - specifier: ^4.6.8 - version: 4.6.8 + specifier: ^4.6.10 + version: 4.6.10 tslib: specifier: ^2.6.2 version: 2.6.2 @@ -26,35 +26,35 @@ devDependencies: specifier: ^0.16.2 version: 0.16.2 '@types/node': - specifier: ^20.6.0 - version: 20.6.0 + specifier: ^20.8.10 + version: 20.8.10 '@typescript-eslint/eslint-plugin': - specifier: ^6.6.0 - version: 6.6.0(@typescript-eslint/parser@6.6.0)(eslint@8.49.0)(typescript@5.2.2) + specifier: ^6.9.1 + version: 6.9.1(@typescript-eslint/parser@6.9.1)(eslint@8.52.0)(typescript@5.2.2) '@typescript-eslint/parser': - specifier: ^6.6.0 - version: 6.6.0(eslint@8.49.0)(typescript@5.2.2) + specifier: ^6.9.1 + version: 6.9.1(eslint@8.52.0)(typescript@5.2.2) c8: specifier: ^8.0.1 version: 8.0.1 eslint: - specifier: ^8.49.0 - version: 8.49.0 + specifier: ^8.52.0 + version: 8.52.0 eslint-plugin-import: - specifier: ^2.28.1 - version: 2.28.1(@typescript-eslint/parser@6.6.0)(eslint@8.49.0) + specifier: ^2.29.0 + version: 2.29.0(@typescript-eslint/parser@6.9.1)(eslint@8.52.0) eslint-plugin-tsdoc: specifier: ^0.2.17 version: 0.2.17 ts-node: specifier: ^10.9.1 - version: 10.9.1(@types/node@20.6.0)(typescript@5.2.2) + version: 10.9.1(@types/node@20.8.10)(typescript@5.2.2) typescript: specifier: ^5.2.2 version: 5.2.2 vitest: - specifier: ^0.34.4 - version: 0.34.4 + specifier: ^0.34.6 + version: 0.34.6 packages: @@ -272,18 +272,18 @@ packages: dev: true optional: true - /@eslint-community/eslint-utils@4.4.0(eslint@8.49.0): + /@eslint-community/eslint-utils@4.4.0(eslint@8.52.0): resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 dependencies: - eslint: 8.49.0 + eslint: 8.52.0 eslint-visitor-keys: 3.4.3 dev: true - /@eslint-community/regexpp@4.8.0: - resolution: {integrity: sha512-JylOEEzDiOryeUnFbQz+oViCXS0KsvR1mvHkoMiu5+UiBvy+RYX7tzlIIIEstF/gVa2tj9AQXk3dgnxv6KxhFg==} + /@eslint-community/regexpp@4.10.0: + resolution: {integrity: sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} dev: true @@ -294,7 +294,7 @@ packages: ajv: 6.12.6 debug: 4.3.4 espree: 9.6.1 - globals: 13.21.0 + globals: 13.23.0 ignore: 5.2.4 import-fresh: 3.3.0 js-yaml: 4.1.0 @@ -304,16 +304,16 @@ packages: - supports-color dev: true - /@eslint/js@8.49.0: - resolution: {integrity: sha512-1S8uAY/MTJqVx0SC4epBq+N2yhuwtNwLbJYNZyhL2pO1ZVKn5HFXav5T41Ryzy9K9V7ZId2JB2oy/W4aCd9/2w==} + /@eslint/js@8.52.0: + resolution: {integrity: sha512-mjZVbpaeMZludF2fsWLD0Z9gCref1Tk4i9+wddjRvpUNqqcndPkBD09N/Mapey0b3jaXbLm2kICwFv2E64QinA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /@humanwhocodes/config-array@0.11.11: - resolution: {integrity: sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==} + /@humanwhocodes/config-array@0.11.13: + resolution: {integrity: sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==} engines: {node: '>=10.10.0'} dependencies: - '@humanwhocodes/object-schema': 1.2.1 + '@humanwhocodes/object-schema': 2.0.1 debug: 4.3.4 minimatch: 3.1.2 transitivePeerDependencies: @@ -325,8 +325,8 @@ packages: engines: {node: '>=12.22'} dev: true - /@humanwhocodes/object-schema@1.2.1: - resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} + /@humanwhocodes/object-schema@2.0.1: + resolution: {integrity: sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==} dev: true /@infinite-fansub/logger@2.2.2: @@ -357,8 +357,8 @@ packages: resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} dev: true - /@jridgewell/trace-mapping@0.3.19: - resolution: {integrity: sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==} + /@jridgewell/trace-mapping@0.3.20: + resolution: {integrity: sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==} dependencies: '@jridgewell/resolve-uri': 3.1.1 '@jridgewell/sourcemap-codec': 1.4.15 @@ -405,16 +405,16 @@ packages: fastq: 1.15.0 dev: true - /@redis/bloom@1.2.0(@redis/client@1.5.9): + /@redis/bloom@1.2.0(@redis/client@1.5.11): resolution: {integrity: sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==} peerDependencies: '@redis/client': ^1.0.0 dependencies: - '@redis/client': 1.5.9 + '@redis/client': 1.5.11 dev: false - /@redis/client@1.5.9: - resolution: {integrity: sha512-SffgN+P1zdWJWSXBvJeynvEnmnZrYmtKSRW00xl8pOPFOMJjxRR9u0frSxJpPR6Y4V+k54blJjGW7FgxbTI7bQ==} + /@redis/client@1.5.11: + resolution: {integrity: sha512-cV7yHcOAtNQ5x/yQl7Yw1xf53kO0FNDTdDU6bFIMbW6ljB7U7ns0YRM+QIkpoqTAt6zK5k9Fq0QWlUbLcq9AvA==} engines: {node: '>=14'} dependencies: cluster-key-slot: 1.1.2 @@ -422,36 +422,36 @@ packages: yallist: 4.0.0 dev: false - /@redis/graph@1.1.0(@redis/client@1.5.9): + /@redis/graph@1.1.0(@redis/client@1.5.11): resolution: {integrity: sha512-16yZWngxyXPd+MJxeSr0dqh2AIOi8j9yXKcKCwVaKDbH3HTuETpDVPcLujhFYVPtYrngSco31BUcSa9TH31Gqg==} peerDependencies: '@redis/client': ^1.0.0 dependencies: - '@redis/client': 1.5.9 + '@redis/client': 1.5.11 dev: false - /@redis/json@1.0.4(@redis/client@1.5.9): - resolution: {integrity: sha512-LUZE2Gdrhg0Rx7AN+cZkb1e6HjoSKaeeW8rYnt89Tly13GBI5eP4CwDVr+MY8BAYfCg4/N15OUrtLoona9uSgw==} + /@redis/json@1.0.6(@redis/client@1.5.11): + resolution: {integrity: sha512-rcZO3bfQbm2zPRpqo82XbW8zg4G/w4W3tI7X8Mqleq9goQjAGLL7q/1n1ZX4dXEAmORVZ4s1+uKLaUOg7LrUhw==} peerDependencies: '@redis/client': ^1.0.0 dependencies: - '@redis/client': 1.5.9 + '@redis/client': 1.5.11 dev: false - /@redis/search@1.1.3(@redis/client@1.5.9): - resolution: {integrity: sha512-4Dg1JjvCevdiCBTZqjhKkGoC5/BcB7k9j99kdMnaXFXg8x4eyOIVg9487CMv7/BUVkFLZCaIh8ead9mU15DNng==} + /@redis/search@1.1.5(@redis/client@1.5.11): + resolution: {integrity: sha512-hPP8w7GfGsbtYEJdn4n7nXa6xt6hVZnnDktKW4ArMaFQ/m/aR7eFvsLQmG/mn1Upq99btPJk+F27IQ2dYpCoUg==} peerDependencies: '@redis/client': ^1.0.0 dependencies: - '@redis/client': 1.5.9 + '@redis/client': 1.5.11 dev: false - /@redis/time-series@1.0.5(@redis/client@1.5.9): + /@redis/time-series@1.0.5(@redis/client@1.5.11): resolution: {integrity: sha512-IFjIgTusQym2B5IZJG3XKr5llka7ey84fw/NOYqESP5WUfQs9zz1ww/9+qoz4ka/S6KcGBodzlCeZ5UImKbscg==} peerDependencies: '@redis/client': ^1.0.0 dependencies: - '@redis/client': 1.5.9 + '@redis/client': 1.5.11 dev: false /@sinclair/typebox@0.27.8: @@ -474,38 +474,40 @@ packages: resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} dev: true - /@types/chai-subset@1.3.3: - resolution: {integrity: sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==} + /@types/chai-subset@1.3.4: + resolution: {integrity: sha512-CCWNXrJYSUIojZ1149ksLl3AN9cmZ5djf+yUoVVV+NuYrtydItQVlL2ZDqyC6M6O9LWRnVf8yYDxbXHO2TfQZg==} dependencies: - '@types/chai': 4.3.6 + '@types/chai': 4.3.9 dev: true - /@types/chai@4.3.6: - resolution: {integrity: sha512-VOVRLM1mBxIRxydiViqPcKn6MIxZytrbMpd6RJLIWKxUNr3zux8no0Oc7kJx0WAPIitgZ0gkrDS+btlqQpubpw==} + /@types/chai@4.3.9: + resolution: {integrity: sha512-69TtiDzu0bcmKQv3yg1Zx409/Kd7r0b5F1PfpYJfSHzLGtB53547V4u+9iqKYsTu/O2ai6KTb0TInNpvuQ3qmg==} dev: true - /@types/istanbul-lib-coverage@2.0.4: - resolution: {integrity: sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==} + /@types/istanbul-lib-coverage@2.0.5: + resolution: {integrity: sha512-zONci81DZYCZjiLe0r6equvZut0b+dBRPBN5kBDjsONnutYNtJMoWQ9uR2RkL1gLG9NMTzvf+29e5RFfPbeKhQ==} dev: true - /@types/json-schema@7.0.12: - resolution: {integrity: sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==} + /@types/json-schema@7.0.14: + resolution: {integrity: sha512-U3PUjAudAdJBeC2pgN8uTIKgxrb4nlDF3SF0++EldXQvQBGkpFZMSnwQiIoDU77tv45VgNkl/L4ouD+rEomujw==} dev: true /@types/json5@0.0.29: resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} dev: true - /@types/node@20.6.0: - resolution: {integrity: sha512-najjVq5KN2vsH2U/xyh2opaSEz6cZMR2SetLIlxlj08nOcmPOemJmUK2o4kUzfLqfrWE0PIrNeE16XhYDd3nqg==} + /@types/node@20.8.10: + resolution: {integrity: sha512-TlgT8JntpcbmKUFzjhsyhGfP2fsiz1Mv56im6enJ905xG1DAYesxJaeSbGqQmAw8OWPdhyJGhGSQGKRNJ45u9w==} + dependencies: + undici-types: 5.26.5 dev: true - /@types/semver@7.5.1: - resolution: {integrity: sha512-cJRQXpObxfNKkFAZbJl2yjWtJCqELQIdShsogr1d2MilP8dKD9TE/nEKHkJgUNHdGKCQaf9HbIynuV2csLGVLg==} + /@types/semver@7.5.4: + resolution: {integrity: sha512-MMzuxN3GdFwskAnb6fz0orFvhfqi752yjaXylr0Rp4oDg5H0Zn1IuyRhDVvYOwAXoJirx2xuS16I3WjxnAIHiQ==} dev: true - /@typescript-eslint/eslint-plugin@6.6.0(@typescript-eslint/parser@6.6.0)(eslint@8.49.0)(typescript@5.2.2): - resolution: {integrity: sha512-CW9YDGTQnNYMIo5lMeuiIG08p4E0cXrXTbcZ2saT/ETE7dWUrNxlijsQeU04qAAKkILiLzdQz+cGFxCJjaZUmA==} + /@typescript-eslint/eslint-plugin@6.9.1(@typescript-eslint/parser@6.9.1)(eslint@8.52.0)(typescript@5.2.2): + resolution: {integrity: sha512-w0tiiRc9I4S5XSXXrMHOWgHgxbrBn1Ro+PmiYhSg2ZVdxrAJtQgzU5o2m1BfP6UOn7Vxcc6152vFjQfmZR4xEg==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha @@ -515,14 +517,14 @@ packages: typescript: optional: true dependencies: - '@eslint-community/regexpp': 4.8.0 - '@typescript-eslint/parser': 6.6.0(eslint@8.49.0)(typescript@5.2.2) - '@typescript-eslint/scope-manager': 6.6.0 - '@typescript-eslint/type-utils': 6.6.0(eslint@8.49.0)(typescript@5.2.2) - '@typescript-eslint/utils': 6.6.0(eslint@8.49.0)(typescript@5.2.2) - '@typescript-eslint/visitor-keys': 6.6.0 + '@eslint-community/regexpp': 4.10.0 + '@typescript-eslint/parser': 6.9.1(eslint@8.52.0)(typescript@5.2.2) + '@typescript-eslint/scope-manager': 6.9.1 + '@typescript-eslint/type-utils': 6.9.1(eslint@8.52.0)(typescript@5.2.2) + '@typescript-eslint/utils': 6.9.1(eslint@8.52.0)(typescript@5.2.2) + '@typescript-eslint/visitor-keys': 6.9.1 debug: 4.3.4 - eslint: 8.49.0 + eslint: 8.52.0 graphemer: 1.4.0 ignore: 5.2.4 natural-compare: 1.4.0 @@ -533,8 +535,8 @@ packages: - supports-color dev: true - /@typescript-eslint/parser@6.6.0(eslint@8.49.0)(typescript@5.2.2): - resolution: {integrity: sha512-setq5aJgUwtzGrhW177/i+DMLqBaJbdwGj2CPIVFFLE0NCliy5ujIdLHd2D1ysmlmsjdL2GWW+hR85neEfc12w==} + /@typescript-eslint/parser@6.9.1(eslint@8.52.0)(typescript@5.2.2): + resolution: {integrity: sha512-C7AK2wn43GSaCUZ9do6Ksgi2g3mwFkMO3Cis96kzmgudoVaKyt62yNzJOktP0HDLb/iO2O0n2lBOzJgr6Q/cyg==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 @@ -543,27 +545,27 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/scope-manager': 6.6.0 - '@typescript-eslint/types': 6.6.0 - '@typescript-eslint/typescript-estree': 6.6.0(typescript@5.2.2) - '@typescript-eslint/visitor-keys': 6.6.0 + '@typescript-eslint/scope-manager': 6.9.1 + '@typescript-eslint/types': 6.9.1 + '@typescript-eslint/typescript-estree': 6.9.1(typescript@5.2.2) + '@typescript-eslint/visitor-keys': 6.9.1 debug: 4.3.4 - eslint: 8.49.0 + eslint: 8.52.0 typescript: 5.2.2 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/scope-manager@6.6.0: - resolution: {integrity: sha512-pT08u5W/GT4KjPUmEtc2kSYvrH8x89cVzkA0Sy2aaOUIw6YxOIjA8ilwLr/1fLjOedX1QAuBpG9XggWqIIfERw==} + /@typescript-eslint/scope-manager@6.9.1: + resolution: {integrity: sha512-38IxvKB6NAne3g/+MyXMs2Cda/Sz+CEpmm+KLGEM8hx/CvnSRuw51i8ukfwB/B/sESdeTGet1NH1Wj7I0YXswg==} engines: {node: ^16.0.0 || >=18.0.0} dependencies: - '@typescript-eslint/types': 6.6.0 - '@typescript-eslint/visitor-keys': 6.6.0 + '@typescript-eslint/types': 6.9.1 + '@typescript-eslint/visitor-keys': 6.9.1 dev: true - /@typescript-eslint/type-utils@6.6.0(eslint@8.49.0)(typescript@5.2.2): - resolution: {integrity: sha512-8m16fwAcEnQc69IpeDyokNO+D5spo0w1jepWWY2Q6y5ZKNuj5EhVQXjtVAeDDqvW6Yg7dhclbsz6rTtOvcwpHg==} + /@typescript-eslint/type-utils@6.9.1(eslint@8.52.0)(typescript@5.2.2): + resolution: {integrity: sha512-eh2oHaUKCK58qIeYp19F5V5TbpM52680sB4zNSz29VBQPTWIlE/hCj5P5B1AChxECe/fmZlspAWFuRniep1Skg==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 @@ -572,23 +574,23 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': 6.6.0(typescript@5.2.2) - '@typescript-eslint/utils': 6.6.0(eslint@8.49.0)(typescript@5.2.2) + '@typescript-eslint/typescript-estree': 6.9.1(typescript@5.2.2) + '@typescript-eslint/utils': 6.9.1(eslint@8.52.0)(typescript@5.2.2) debug: 4.3.4 - eslint: 8.49.0 + eslint: 8.52.0 ts-api-utils: 1.0.3(typescript@5.2.2) typescript: 5.2.2 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/types@6.6.0: - resolution: {integrity: sha512-CB6QpJQ6BAHlJXdwUmiaXDBmTqIE2bzGTDLADgvqtHWuhfNP3rAOK7kAgRMAET5rDRr9Utt+qAzRBdu3AhR3sg==} + /@typescript-eslint/types@6.9.1: + resolution: {integrity: sha512-BUGslGOb14zUHOUmDB2FfT6SI1CcZEJYfF3qFwBeUrU6srJfzANonwRYHDpLBuzbq3HaoF2XL2hcr01c8f8OaQ==} engines: {node: ^16.0.0 || >=18.0.0} dev: true - /@typescript-eslint/typescript-estree@6.6.0(typescript@5.2.2): - resolution: {integrity: sha512-hMcTQ6Al8MP2E6JKBAaSxSVw5bDhdmbCEhGW/V8QXkb9oNsFkA4SBuOMYVPxD3jbtQ4R/vSODBsr76R6fP3tbA==} + /@typescript-eslint/typescript-estree@6.9.1(typescript@5.2.2): + resolution: {integrity: sha512-U+mUylTHfcqeO7mLWVQ5W/tMLXqVpRv61wm9ZtfE5egz7gtnmqVIw9ryh0mgIlkKk9rZLY3UHygsBSdB9/ftyw==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: typescript: '*' @@ -596,8 +598,8 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/types': 6.6.0 - '@typescript-eslint/visitor-keys': 6.6.0 + '@typescript-eslint/types': 6.9.1 + '@typescript-eslint/visitor-keys': 6.9.1 debug: 4.3.4 globby: 11.1.0 is-glob: 4.0.3 @@ -608,86 +610,90 @@ packages: - supports-color dev: true - /@typescript-eslint/utils@6.6.0(eslint@8.49.0)(typescript@5.2.2): - resolution: {integrity: sha512-mPHFoNa2bPIWWglWYdR0QfY9GN0CfvvXX1Sv6DlSTive3jlMTUy+an67//Gysc+0Me9pjitrq0LJp0nGtLgftw==} + /@typescript-eslint/utils@6.9.1(eslint@8.52.0)(typescript@5.2.2): + resolution: {integrity: sha512-L1T0A5nFdQrMVunpZgzqPL6y2wVreSyHhKGZryS6jrEN7bD9NplVAyMryUhXsQ4TWLnZmxc2ekar/lSGIlprCA==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.49.0) - '@types/json-schema': 7.0.12 - '@types/semver': 7.5.1 - '@typescript-eslint/scope-manager': 6.6.0 - '@typescript-eslint/types': 6.6.0 - '@typescript-eslint/typescript-estree': 6.6.0(typescript@5.2.2) - eslint: 8.49.0 + '@eslint-community/eslint-utils': 4.4.0(eslint@8.52.0) + '@types/json-schema': 7.0.14 + '@types/semver': 7.5.4 + '@typescript-eslint/scope-manager': 6.9.1 + '@typescript-eslint/types': 6.9.1 + '@typescript-eslint/typescript-estree': 6.9.1(typescript@5.2.2) + eslint: 8.52.0 semver: 7.5.4 transitivePeerDependencies: - supports-color - typescript dev: true - /@typescript-eslint/visitor-keys@6.6.0: - resolution: {integrity: sha512-L61uJT26cMOfFQ+lMZKoJNbAEckLe539VhTxiGHrWl5XSKQgA0RTBZJW2HFPy5T0ZvPVSD93QsrTKDkfNwJGyQ==} + /@typescript-eslint/visitor-keys@6.9.1: + resolution: {integrity: sha512-MUaPUe/QRLEffARsmNfmpghuQkW436DvESW+h+M52w0coICHRfD6Np9/K6PdACwnrq1HmuLl+cSPZaJmeVPkSw==} engines: {node: ^16.0.0 || >=18.0.0} dependencies: - '@typescript-eslint/types': 6.6.0 + '@typescript-eslint/types': 6.9.1 eslint-visitor-keys: 3.4.3 dev: true - /@vitest/expect@0.34.4: - resolution: {integrity: sha512-XlMKX8HyYUqB8dsY8Xxrc64J2Qs9pKMt2Z8vFTL4mBWXJsg4yoALHzJfDWi8h5nkO4Zua4zjqtapQ/IluVkSnA==} + /@ungap/structured-clone@1.2.0: + resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} + dev: true + + /@vitest/expect@0.34.6: + resolution: {integrity: sha512-QUzKpUQRc1qC7qdGo7rMK3AkETI7w18gTCUrsNnyjjJKYiuUB9+TQK3QnR1unhCnWRC0AbKv2omLGQDF/mIjOw==} dependencies: - '@vitest/spy': 0.34.4 - '@vitest/utils': 0.34.4 - chai: 4.3.8 + '@vitest/spy': 0.34.6 + '@vitest/utils': 0.34.6 + chai: 4.3.10 dev: true - /@vitest/runner@0.34.4: - resolution: {integrity: sha512-hwwdB1StERqUls8oV8YcpmTIpVeJMe4WgYuDongVzixl5hlYLT2G8afhcdADeDeqCaAmZcSgLTLtqkjPQF7x+w==} + /@vitest/runner@0.34.6: + resolution: {integrity: sha512-1CUQgtJSLF47NnhN+F9X2ycxUP0kLHQ/JWvNHbeBfwW8CzEGgeskzNnHDyv1ieKTltuR6sdIHV+nmR6kPxQqzQ==} dependencies: - '@vitest/utils': 0.34.4 + '@vitest/utils': 0.34.6 p-limit: 4.0.0 pathe: 1.1.1 dev: true - /@vitest/snapshot@0.34.4: - resolution: {integrity: sha512-GCsh4coc3YUSL/o+BPUo7lHQbzpdttTxL6f4q0jRx2qVGoYz/cyTRDJHbnwks6TILi6560bVWoBpYC10PuTLHw==} + /@vitest/snapshot@0.34.6: + resolution: {integrity: sha512-B3OZqYn6k4VaN011D+ve+AA4whM4QkcwcrwaKwAbyyvS/NB1hCWjFIBQxAQQSQir9/RtyAAGuq+4RJmbn2dH4w==} dependencies: - magic-string: 0.30.3 + magic-string: 0.30.5 pathe: 1.1.1 - pretty-format: 29.6.3 + pretty-format: 29.7.0 dev: true - /@vitest/spy@0.34.4: - resolution: {integrity: sha512-PNU+fd7DUPgA3Ya924b1qKuQkonAW6hL7YUjkON3wmBwSTIlhOSpy04SJ0NrRsEbrXgMMj6Morh04BMf8k+w0g==} + /@vitest/spy@0.34.6: + resolution: {integrity: sha512-xaCvneSaeBw/cz8ySmF7ZwGvL0lBjfvqc1LpQ/vcdHEvpLn3Ff1vAvjw+CoGn0802l++5L/pxb7whwcWAw+DUQ==} dependencies: - tinyspy: 2.1.1 + tinyspy: 2.2.0 dev: true - /@vitest/utils@0.34.4: - resolution: {integrity: sha512-yR2+5CHhp/K4ySY0Qtd+CAL9f5Yh1aXrKfAT42bq6CtlGPh92jIDDDSg7ydlRow1CP+dys4TrOrbELOyNInHSg==} + /@vitest/utils@0.34.6: + resolution: {integrity: sha512-IG5aDD8S6zlvloDsnzHw0Ut5xczlF+kv2BOTo+iXfPr54Yhi5qbVOgGB1hZaVq4iJ4C/MZ2J0y15IlsV/ZcI0A==} dependencies: diff-sequences: 29.6.3 - loupe: 2.3.6 - pretty-format: 29.6.3 + loupe: 2.3.7 + pretty-format: 29.7.0 dev: true - /acorn-jsx@5.3.2(acorn@8.10.0): + /acorn-jsx@5.3.2(acorn@8.11.2): resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: - acorn: 8.10.0 + acorn: 8.11.2 dev: true - /acorn-walk@8.2.0: - resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} + /acorn-walk@8.3.0: + resolution: {integrity: sha512-FS7hV565M5l1R08MXqo8odwMTB02C2UqzB17RVgu9EyuYFBqJZ3/ZY97sQD5FewVu1UyDFc1yztUDrAwT0EypA==} engines: {node: '>=0.4.0'} dev: true - /acorn@8.10.0: - resolution: {integrity: sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==} + /acorn@8.11.2: + resolution: {integrity: sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==} engines: {node: '>=0.4.0'} hasBin: true dev: true @@ -729,7 +735,7 @@ packages: /array-buffer-byte-length@1.0.0: resolution: {integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==} dependencies: - call-bind: 1.0.2 + call-bind: 1.0.5 is-array-buffer: 3.0.2 dev: true @@ -737,10 +743,10 @@ packages: resolution: {integrity: sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.22.1 - get-intrinsic: 1.2.1 + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + get-intrinsic: 1.2.2 is-string: 1.0.7 dev: true @@ -753,31 +759,31 @@ packages: resolution: {integrity: sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.22.1 - es-shim-unscopables: 1.0.0 - get-intrinsic: 1.2.1 + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + es-shim-unscopables: 1.0.2 + get-intrinsic: 1.2.2 dev: true /array.prototype.flat@1.3.2: resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.22.1 - es-shim-unscopables: 1.0.0 + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + es-shim-unscopables: 1.0.2 dev: true /array.prototype.flatmap@1.3.2: resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.22.1 - es-shim-unscopables: 1.0.0 + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + es-shim-unscopables: 1.0.2 dev: true /arraybuffer.prototype.slice@1.0.2: @@ -785,10 +791,10 @@ packages: engines: {node: '>= 0.4'} dependencies: array-buffer-byte-length: 1.0.0 - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.22.1 - get-intrinsic: 1.2.1 + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + get-intrinsic: 1.2.2 is-array-buffer: 3.0.2 is-shared-array-buffer: 1.0.2 dev: true @@ -834,7 +840,7 @@ packages: istanbul-reports: 3.1.6 rimraf: 3.0.2 test-exclude: 6.0.0 - v8-to-istanbul: 9.1.0 + v8-to-istanbul: 9.1.3 yargs: 17.7.2 yargs-parser: 21.1.1 dev: true @@ -844,11 +850,12 @@ packages: engines: {node: '>=8'} dev: true - /call-bind@1.0.2: - resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} + /call-bind@1.0.5: + resolution: {integrity: sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==} dependencies: - function-bind: 1.1.1 - get-intrinsic: 1.2.1 + function-bind: 1.1.2 + get-intrinsic: 1.2.2 + set-function-length: 1.1.1 dev: true /callsites@3.1.0: @@ -856,15 +863,15 @@ packages: engines: {node: '>=6'} dev: true - /chai@4.3.8: - resolution: {integrity: sha512-vX4YvVVtxlfSZ2VecZgFUTU5qPCYsobVI2O9FmwEXBhDigYGQA6jRXCycIs1yJnnWbZ6/+a2zNIF5DfVCcJBFQ==} + /chai@4.3.10: + resolution: {integrity: sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==} engines: {node: '>=4'} dependencies: assertion-error: 1.1.0 - check-error: 1.0.2 + check-error: 1.0.3 deep-eql: 4.1.3 - get-func-name: 2.0.0 - loupe: 2.3.6 + get-func-name: 2.0.2 + loupe: 2.3.7 pathval: 1.1.1 type-detect: 4.0.8 dev: true @@ -877,8 +884,10 @@ packages: supports-color: 7.2.0 dev: true - /check-error@1.0.2: - resolution: {integrity: sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==} + /check-error@1.0.3: + resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} + dependencies: + get-func-name: 2.0.2 dev: true /cliui@8.0.1: @@ -914,8 +923,8 @@ packages: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} dev: true - /convert-source-map@1.9.0: - resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} + /convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} dev: true /create-require@1.1.1: @@ -965,11 +974,21 @@ packages: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} dev: true - /define-properties@1.2.0: - resolution: {integrity: sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==} + /define-data-property@1.1.1: + resolution: {integrity: sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.2.2 + gopd: 1.0.1 + has-property-descriptors: 1.0.1 + dev: true + + /define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} dependencies: - has-property-descriptors: 1.0.0 + define-data-property: 1.1.1 + has-property-descriptors: 1.0.1 object-keys: 1.1.1 dev: true @@ -1008,26 +1027,26 @@ packages: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} dev: true - /es-abstract@1.22.1: - resolution: {integrity: sha512-ioRRcXMO6OFyRpyzV3kE1IIBd4WG5/kltnzdxSCqoP8CMGs/Li+M1uF5o7lOkZVFjDs+NLesthnF66Pg/0q0Lw==} + /es-abstract@1.22.3: + resolution: {integrity: sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==} engines: {node: '>= 0.4'} dependencies: array-buffer-byte-length: 1.0.0 arraybuffer.prototype.slice: 1.0.2 available-typed-arrays: 1.0.5 - call-bind: 1.0.2 - es-set-tostringtag: 2.0.1 + call-bind: 1.0.5 + es-set-tostringtag: 2.0.2 es-to-primitive: 1.2.1 function.prototype.name: 1.1.6 - get-intrinsic: 1.2.1 + get-intrinsic: 1.2.2 get-symbol-description: 1.0.0 globalthis: 1.0.3 gopd: 1.0.1 - has: 1.0.3 - has-property-descriptors: 1.0.0 + has-property-descriptors: 1.0.1 has-proto: 1.0.1 has-symbols: 1.0.3 - internal-slot: 1.0.5 + hasown: 2.0.0 + internal-slot: 1.0.6 is-array-buffer: 3.0.2 is-callable: 1.2.7 is-negative-zero: 2.0.2 @@ -1036,10 +1055,10 @@ packages: is-string: 1.0.7 is-typed-array: 1.1.12 is-weakref: 1.0.2 - object-inspect: 1.12.3 + object-inspect: 1.13.1 object-keys: 1.1.1 object.assign: 4.1.4 - regexp.prototype.flags: 1.5.0 + regexp.prototype.flags: 1.5.1 safe-array-concat: 1.0.1 safe-regex-test: 1.0.0 string.prototype.trim: 1.2.8 @@ -1050,22 +1069,22 @@ packages: typed-array-byte-offset: 1.0.0 typed-array-length: 1.0.4 unbox-primitive: 1.0.2 - which-typed-array: 1.1.11 + which-typed-array: 1.1.13 dev: true - /es-set-tostringtag@2.0.1: - resolution: {integrity: sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==} + /es-set-tostringtag@2.0.2: + resolution: {integrity: sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==} engines: {node: '>= 0.4'} dependencies: - get-intrinsic: 1.2.1 - has: 1.0.3 + get-intrinsic: 1.2.2 has-tostringtag: 1.0.0 + hasown: 2.0.0 dev: true - /es-shim-unscopables@1.0.0: - resolution: {integrity: sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==} + /es-shim-unscopables@1.0.2: + resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==} dependencies: - has: 1.0.3 + hasown: 2.0.0 dev: true /es-to-primitive@1.2.1: @@ -1121,13 +1140,13 @@ packages: resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} dependencies: debug: 3.2.7 - is-core-module: 2.13.0 - resolve: 1.22.4 + is-core-module: 2.13.1 + resolve: 1.22.8 transitivePeerDependencies: - supports-color dev: true - /eslint-module-utils@2.8.0(@typescript-eslint/parser@6.6.0)(eslint-import-resolver-node@0.3.9)(eslint@8.49.0): + /eslint-module-utils@2.8.0(@typescript-eslint/parser@6.9.1)(eslint-import-resolver-node@0.3.9)(eslint@8.52.0): resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==} engines: {node: '>=4'} peerDependencies: @@ -1148,16 +1167,16 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 6.6.0(eslint@8.49.0)(typescript@5.2.2) + '@typescript-eslint/parser': 6.9.1(eslint@8.52.0)(typescript@5.2.2) debug: 3.2.7 - eslint: 8.49.0 + eslint: 8.52.0 eslint-import-resolver-node: 0.3.9 transitivePeerDependencies: - supports-color dev: true - /eslint-plugin-import@2.28.1(@typescript-eslint/parser@6.6.0)(eslint@8.49.0): - resolution: {integrity: sha512-9I9hFlITvOV55alzoKBI+K9q74kv0iKMeY6av5+umsNwayt59fz692daGyjR+oStBQgx6nwR9rXldDev3Clw+A==} + /eslint-plugin-import@2.29.0(@typescript-eslint/parser@6.9.1)(eslint@8.52.0): + resolution: {integrity: sha512-QPOO5NO6Odv5lpoTkddtutccQjysJuFxoPS7fAHO+9m9udNHvTCPSAMW9zGAYj8lAIdr40I8yPCdUYrncXtrwg==} engines: {node: '>=4'} peerDependencies: '@typescript-eslint/parser': '*' @@ -1166,18 +1185,18 @@ packages: '@typescript-eslint/parser': optional: true dependencies: - '@typescript-eslint/parser': 6.6.0(eslint@8.49.0)(typescript@5.2.2) + '@typescript-eslint/parser': 6.9.1(eslint@8.52.0)(typescript@5.2.2) array-includes: 3.1.7 array.prototype.findlastindex: 1.2.3 array.prototype.flat: 1.3.2 array.prototype.flatmap: 1.3.2 debug: 3.2.7 doctrine: 2.1.0 - eslint: 8.49.0 + eslint: 8.52.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.6.0)(eslint-import-resolver-node@0.3.9)(eslint@8.49.0) - has: 1.0.3 - is-core-module: 2.13.0 + eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.9.1)(eslint-import-resolver-node@0.3.9)(eslint@8.52.0) + hasown: 2.0.0 + is-core-module: 2.13.1 is-glob: 4.0.3 minimatch: 3.1.2 object.fromentries: 2.0.7 @@ -1211,18 +1230,19 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /eslint@8.49.0: - resolution: {integrity: sha512-jw03ENfm6VJI0jA9U+8H5zfl5b+FvuU3YYvZRdZHOlU2ggJkxrlkJH4HcDrZpj6YwD8kuYqvQM8LyesoazrSOQ==} + /eslint@8.52.0: + resolution: {integrity: sha512-zh/JHnaixqHZsolRB/w9/02akBk9EPrOs9JwcTP2ek7yL5bVvXuRariiaAjjoJ5DvuwQ1WAE/HsMz+w17YgBCg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} hasBin: true dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.49.0) - '@eslint-community/regexpp': 4.8.0 + '@eslint-community/eslint-utils': 4.4.0(eslint@8.52.0) + '@eslint-community/regexpp': 4.10.0 '@eslint/eslintrc': 2.1.2 - '@eslint/js': 8.49.0 - '@humanwhocodes/config-array': 0.11.11 + '@eslint/js': 8.52.0 + '@humanwhocodes/config-array': 0.11.13 '@humanwhocodes/module-importer': 1.0.1 '@nodelib/fs.walk': 1.2.8 + '@ungap/structured-clone': 1.2.0 ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.3 @@ -1238,7 +1258,7 @@ packages: file-entry-cache: 6.0.1 find-up: 5.0.0 glob-parent: 6.0.2 - globals: 13.21.0 + globals: 13.23.0 graphemer: 1.4.0 ignore: 5.2.4 imurmurhash: 0.1.4 @@ -1261,8 +1281,8 @@ packages: resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: - acorn: 8.10.0 - acorn-jsx: 5.3.2(acorn@8.10.0) + acorn: 8.11.2 + acorn-jsx: 5.3.2(acorn@8.11.2) eslint-visitor-keys: 3.4.3 dev: true @@ -1323,7 +1343,7 @@ packages: resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} engines: {node: ^10.12.0 || >=12.0.0} dependencies: - flat-cache: 3.1.0 + flat-cache: 3.1.1 dev: true /fill-range@7.0.1: @@ -1341,17 +1361,17 @@ packages: path-exists: 4.0.0 dev: true - /flat-cache@3.1.0: - resolution: {integrity: sha512-OHx4Qwrrt0E4jEIcI5/Xb+f+QmJYNj2rrK8wiIdQOIrB9WrrJL8cjZvXdXuBTkkEwEqLycb5BeZDV1o2i9bTew==} + /flat-cache@3.1.1: + resolution: {integrity: sha512-/qM2b3LUIaIgviBQovTLvijfyOQXPtSRnRK26ksj2J7rzPIecePUIpJsZ4T02Qg+xiAEKIs5K8dsHEd+VaKa/Q==} engines: {node: '>=12.0.0'} dependencies: - flatted: 3.2.7 - keyv: 4.5.3 + flatted: 3.2.9 + keyv: 4.5.4 rimraf: 3.0.2 dev: true - /flatted@3.2.7: - resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==} + /flatted@3.2.9: + resolution: {integrity: sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==} dev: true /for-each@0.3.3: @@ -1380,17 +1400,17 @@ packages: dev: true optional: true - /function-bind@1.1.1: - resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} + /function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} dev: true /function.prototype.name@1.1.6: resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.22.1 + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 functions-have-names: 1.2.3 dev: true @@ -1408,25 +1428,25 @@ packages: engines: {node: 6.* || 8.* || >= 10.*} dev: true - /get-func-name@2.0.0: - resolution: {integrity: sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==} + /get-func-name@2.0.2: + resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} dev: true - /get-intrinsic@1.2.1: - resolution: {integrity: sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==} + /get-intrinsic@1.2.2: + resolution: {integrity: sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==} dependencies: - function-bind: 1.1.1 - has: 1.0.3 + function-bind: 1.1.2 has-proto: 1.0.1 has-symbols: 1.0.3 + hasown: 2.0.0 dev: true /get-symbol-description@1.0.0: resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.2.1 + call-bind: 1.0.5 + get-intrinsic: 1.2.2 dev: true /glob-parent@5.1.2: @@ -1454,8 +1474,8 @@ packages: path-is-absolute: 1.0.1 dev: true - /globals@13.21.0: - resolution: {integrity: sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==} + /globals@13.23.0: + resolution: {integrity: sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==} engines: {node: '>=8'} dependencies: type-fest: 0.20.2 @@ -1465,7 +1485,7 @@ packages: resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==} engines: {node: '>= 0.4'} dependencies: - define-properties: 1.2.0 + define-properties: 1.2.1 dev: true /globby@11.1.0: @@ -1483,7 +1503,7 @@ packages: /gopd@1.0.1: resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} dependencies: - get-intrinsic: 1.2.1 + get-intrinsic: 1.2.2 dev: true /graphemer@1.4.0: @@ -1499,10 +1519,10 @@ packages: engines: {node: '>=8'} dev: true - /has-property-descriptors@1.0.0: - resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==} + /has-property-descriptors@1.0.1: + resolution: {integrity: sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==} dependencies: - get-intrinsic: 1.2.1 + get-intrinsic: 1.2.2 dev: true /has-proto@1.0.1: @@ -1522,11 +1542,11 @@ packages: has-symbols: 1.0.3 dev: true - /has@1.0.3: - resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} - engines: {node: '>= 0.4.0'} + /hasown@2.0.0: + resolution: {integrity: sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==} + engines: {node: '>= 0.4'} dependencies: - function-bind: 1.1.1 + function-bind: 1.1.2 dev: true /html-escaper@2.0.2: @@ -1562,20 +1582,20 @@ packages: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} dev: true - /internal-slot@1.0.5: - resolution: {integrity: sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==} + /internal-slot@1.0.6: + resolution: {integrity: sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==} engines: {node: '>= 0.4'} dependencies: - get-intrinsic: 1.2.1 - has: 1.0.3 + get-intrinsic: 1.2.2 + hasown: 2.0.0 side-channel: 1.0.4 dev: true /is-array-buffer@3.0.2: resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==} dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.2.1 + call-bind: 1.0.5 + get-intrinsic: 1.2.2 is-typed-array: 1.1.12 dev: true @@ -1589,7 +1609,7 @@ packages: resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 + call-bind: 1.0.5 has-tostringtag: 1.0.0 dev: true @@ -1598,10 +1618,10 @@ packages: engines: {node: '>= 0.4'} dev: true - /is-core-module@2.13.0: - resolution: {integrity: sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==} + /is-core-module@2.13.1: + resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} dependencies: - has: 1.0.3 + hasown: 2.0.0 dev: true /is-date-object@1.0.5: @@ -1654,14 +1674,14 @@ packages: resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 + call-bind: 1.0.5 has-tostringtag: 1.0.0 dev: true /is-shared-array-buffer@1.0.2: resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==} dependencies: - call-bind: 1.0.2 + call-bind: 1.0.5 dev: true /is-string@1.0.7: @@ -1682,13 +1702,13 @@ packages: resolution: {integrity: sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==} engines: {node: '>= 0.4'} dependencies: - which-typed-array: 1.1.11 + which-typed-array: 1.1.13 dev: true /is-weakref@1.0.2: resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} dependencies: - call-bind: 1.0.2 + call-bind: 1.0.5 dev: true /isarray@2.0.5: @@ -1755,8 +1775,8 @@ packages: resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==} dev: true - /keyv@4.5.3: - resolution: {integrity: sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==} + /keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} dependencies: json-buffer: 3.0.1 dev: true @@ -1785,10 +1805,10 @@ packages: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} dev: true - /loupe@2.3.6: - resolution: {integrity: sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==} + /loupe@2.3.7: + resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} dependencies: - get-func-name: 2.0.0 + get-func-name: 2.0.2 dev: true /lru-cache@6.0.0: @@ -1798,8 +1818,8 @@ packages: yallist: 4.0.0 dev: true - /magic-string@0.30.3: - resolution: {integrity: sha512-B7xGbll2fG/VjP+SWg4sX3JynwIU0mjoTc6MPpKNuIvftk6u6vqhDnk1R80b8C2GBR6ywqy+1DcKBrevBg+bmw==} + /magic-string@0.30.5: + resolution: {integrity: sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==} engines: {node: '>=12'} dependencies: '@jridgewell/sourcemap-codec': 1.4.15 @@ -1842,10 +1862,10 @@ packages: /mlly@1.4.2: resolution: {integrity: sha512-i/Ykufi2t1EZ6NaPLdfnZk2AX8cs0d+mTzVKuPfqPKPatxLApaBoxJQ9x1/uckXtrS/U5oisPMDkNs0yQTaBRg==} dependencies: - acorn: 8.10.0 + acorn: 8.11.2 pathe: 1.1.1 pkg-types: 1.0.3 - ufo: 1.3.0 + ufo: 1.3.1 dev: true /ms@2.1.2: @@ -1866,8 +1886,8 @@ packages: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} dev: true - /object-inspect@1.12.3: - resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==} + /object-inspect@1.13.1: + resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} dev: true /object-keys@1.1.1: @@ -1879,8 +1899,8 @@ packages: resolution: {integrity: sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 + call-bind: 1.0.5 + define-properties: 1.2.1 has-symbols: 1.0.3 object-keys: 1.1.1 dev: true @@ -1889,27 +1909,27 @@ packages: resolution: {integrity: sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.22.1 + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 dev: true /object.groupby@1.0.1: resolution: {integrity: sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ==} dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.22.1 - get-intrinsic: 1.2.1 + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + get-intrinsic: 1.2.2 dev: true /object.values@1.1.7: resolution: {integrity: sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.22.1 + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 dev: true /once@1.4.0: @@ -2007,8 +2027,8 @@ packages: pathe: 1.1.1 dev: true - /postcss@8.4.29: - resolution: {integrity: sha512-cbI+jaqIeu/VGqXEarWkRCCffhjgXc0qjBtXpqJhTBohMUjUQnbBr0xqX3vEKudc4iviTewcJo5ajcec5+wdJw==} + /postcss@8.4.31: + resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} engines: {node: ^10 || ^12 || >=14} dependencies: nanoid: 3.3.6 @@ -2021,8 +2041,8 @@ packages: engines: {node: '>= 0.8.0'} dev: true - /pretty-format@29.6.3: - resolution: {integrity: sha512-ZsBgjVhFAj5KeK+nHfF1305/By3lechHQSMWCTl8iHSbfOm2TN5nHEtFc/+W7fAyUeCs2n5iow72gld4gW0xDw==} + /pretty-format@29.7.0: + resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/schemas': 29.6.3 @@ -2030,8 +2050,8 @@ packages: react-is: 18.2.0 dev: true - /punycode@2.3.0: - resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} + /punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} dev: true @@ -2043,24 +2063,24 @@ packages: resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} dev: true - /redis@4.6.8: - resolution: {integrity: sha512-S7qNkPUYrsofQ0ztWlTHSaK0Qqfl1y+WMIxrzeAGNG+9iUZB4HGeBgkHxE6uJJ6iXrkvLd1RVJ2nvu6H1sAzfQ==} + /redis@4.6.10: + resolution: {integrity: sha512-mmbyhuKgDiJ5TWUhiKhBssz+mjsuSI/lSZNPI9QvZOYzWvYGejtb+W3RlDDf8LD6Bdl5/mZeG8O1feUGhXTxEg==} dependencies: - '@redis/bloom': 1.2.0(@redis/client@1.5.9) - '@redis/client': 1.5.9 - '@redis/graph': 1.1.0(@redis/client@1.5.9) - '@redis/json': 1.0.4(@redis/client@1.5.9) - '@redis/search': 1.1.3(@redis/client@1.5.9) - '@redis/time-series': 1.0.5(@redis/client@1.5.9) + '@redis/bloom': 1.2.0(@redis/client@1.5.11) + '@redis/client': 1.5.11 + '@redis/graph': 1.1.0(@redis/client@1.5.11) + '@redis/json': 1.0.6(@redis/client@1.5.11) + '@redis/search': 1.1.5(@redis/client@1.5.11) + '@redis/time-series': 1.0.5(@redis/client@1.5.11) dev: false - /regexp.prototype.flags@1.5.0: - resolution: {integrity: sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==} + /regexp.prototype.flags@1.5.1: + resolution: {integrity: sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - functions-have-names: 1.2.3 + call-bind: 1.0.5 + define-properties: 1.2.1 + set-function-name: 2.0.1 dev: true /require-directory@2.1.1: @@ -2076,15 +2096,15 @@ packages: /resolve@1.19.0: resolution: {integrity: sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==} dependencies: - is-core-module: 2.13.0 + is-core-module: 2.13.1 path-parse: 1.0.7 dev: true - /resolve@1.22.4: - resolution: {integrity: sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==} + /resolve@1.22.8: + resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} hasBin: true dependencies: - is-core-module: 2.13.0 + is-core-module: 2.13.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 dev: true @@ -2101,8 +2121,8 @@ packages: glob: 7.2.3 dev: true - /rollup@3.29.0: - resolution: {integrity: sha512-nszM8DINnx1vSS+TpbWKMkxem0CDWk3cSit/WWCBVs9/JZ1I/XLwOsiUglYuYReaeWWSsW9kge5zE5NZtf/a4w==} + /rollup@3.29.4: + resolution: {integrity: sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==} engines: {node: '>=14.18.0', npm: '>=8.0.0'} hasBin: true optionalDependencies: @@ -2119,8 +2139,8 @@ packages: resolution: {integrity: sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==} engines: {node: '>=0.4'} dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.2.1 + call-bind: 1.0.5 + get-intrinsic: 1.2.2 has-symbols: 1.0.3 isarray: 2.0.5 dev: true @@ -2128,8 +2148,8 @@ packages: /safe-regex-test@1.0.0: resolution: {integrity: sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==} dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.2.1 + call-bind: 1.0.5 + get-intrinsic: 1.2.2 is-regex: 1.1.4 dev: true @@ -2146,6 +2166,25 @@ packages: lru-cache: 6.0.0 dev: true + /set-function-length@1.1.1: + resolution: {integrity: sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==} + engines: {node: '>= 0.4'} + dependencies: + define-data-property: 1.1.1 + get-intrinsic: 1.2.2 + gopd: 1.0.1 + has-property-descriptors: 1.0.1 + dev: true + + /set-function-name@2.0.1: + resolution: {integrity: sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==} + engines: {node: '>= 0.4'} + dependencies: + define-data-property: 1.1.1 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.1 + dev: true + /shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -2161,9 +2200,9 @@ packages: /side-channel@1.0.4: resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.2.1 - object-inspect: 1.12.3 + call-bind: 1.0.5 + get-intrinsic: 1.2.2 + object-inspect: 1.13.1 dev: true /siginfo@2.0.0: @@ -2205,25 +2244,25 @@ packages: resolution: {integrity: sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.22.1 + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 dev: true /string.prototype.trimend@1.0.7: resolution: {integrity: sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==} dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.22.1 + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 dev: true /string.prototype.trimstart@1.0.7: resolution: {integrity: sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==} dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.22.1 + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 dev: true /strip-ansi@6.0.1: @@ -2246,7 +2285,7 @@ packages: /strip-literal@1.3.0: resolution: {integrity: sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==} dependencies: - acorn: 8.10.0 + acorn: 8.11.2 dev: true /supports-color@7.2.0: @@ -2274,8 +2313,8 @@ packages: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} dev: true - /tinybench@2.5.0: - resolution: {integrity: sha512-kRwSG8Zx4tjF9ZiyH4bhaebu+EDz1BOx9hOigYHlUW4xxI/wKIUQUqo018UlU4ar6ATPBsaMrdbKZ+tmPdohFA==} + /tinybench@2.5.1: + resolution: {integrity: sha512-65NKvSuAVDP/n4CqH+a9w2kTlLReS9vhsAP06MWx+/89nMinJyB2icyl58RIcqCmIggpojIGeuJGhjU1aGMBSg==} dev: true /tinypool@0.7.0: @@ -2283,8 +2322,8 @@ packages: engines: {node: '>=14.0.0'} dev: true - /tinyspy@2.1.1: - resolution: {integrity: sha512-XPJL2uSzcOyBMky6OFrusqWlzfFrXtE0hPuMgW8A2HmaqrPo4ZQHRN/V0QXN3FSjKxpsbRrFc5LI7KOwBsT1/w==} + /tinyspy@2.2.0: + resolution: {integrity: sha512-d2eda04AN/cPOR89F7Xv5bK/jrQEhmcLFe6HFldoeO9AJtps+fqEnh486vnT/8y4bw38pSyxDcTCAq+Ks2aJTg==} engines: {node: '>=14.0.0'} dev: true @@ -2304,7 +2343,7 @@ packages: typescript: 5.2.2 dev: true - /ts-node@10.9.1(@types/node@20.6.0)(typescript@5.2.2): + /ts-node@10.9.1(@types/node@20.8.10)(typescript@5.2.2): resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} hasBin: true peerDependencies: @@ -2323,9 +2362,9 @@ packages: '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 20.6.0 - acorn: 8.10.0 - acorn-walk: 8.2.0 + '@types/node': 20.8.10 + acorn: 8.11.2 + acorn-walk: 8.3.0 arg: 4.1.3 create-require: 1.1.1 diff: 4.0.2 @@ -2369,8 +2408,8 @@ packages: resolution: {integrity: sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.2.1 + call-bind: 1.0.5 + get-intrinsic: 1.2.2 is-typed-array: 1.1.12 dev: true @@ -2378,7 +2417,7 @@ packages: resolution: {integrity: sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 + call-bind: 1.0.5 for-each: 0.3.3 has-proto: 1.0.1 is-typed-array: 1.1.12 @@ -2389,7 +2428,7 @@ packages: engines: {node: '>= 0.4'} dependencies: available-typed-arrays: 1.0.5 - call-bind: 1.0.2 + call-bind: 1.0.5 for-each: 0.3.3 has-proto: 1.0.1 is-typed-array: 1.1.12 @@ -2398,7 +2437,7 @@ packages: /typed-array-length@1.0.4: resolution: {integrity: sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==} dependencies: - call-bind: 1.0.2 + call-bind: 1.0.5 for-each: 0.3.3 is-typed-array: 1.1.12 dev: true @@ -2409,40 +2448,44 @@ packages: hasBin: true dev: true - /ufo@1.3.0: - resolution: {integrity: sha512-bRn3CsoojyNStCZe0BG0Mt4Nr/4KF+rhFlnNXybgqt5pXHNFRlqinSoQaTrGyzE4X8aHplSb+TorH+COin9Yxw==} + /ufo@1.3.1: + resolution: {integrity: sha512-uY/99gMLIOlJPwATcMVYfqDSxUR9//AUcgZMzwfSTJPDKzA1S8mX4VLqa+fiAtveraQUBCz4FFcwVZBGbwBXIw==} dev: true /unbox-primitive@1.0.2: resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} dependencies: - call-bind: 1.0.2 + call-bind: 1.0.5 has-bigints: 1.0.2 has-symbols: 1.0.3 which-boxed-primitive: 1.0.2 dev: true + /undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + dev: true + /uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} dependencies: - punycode: 2.3.0 + punycode: 2.3.1 dev: true /v8-compile-cache-lib@3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} dev: true - /v8-to-istanbul@9.1.0: - resolution: {integrity: sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==} + /v8-to-istanbul@9.1.3: + resolution: {integrity: sha512-9lDD+EVI2fjFsMWXc6dy5JJzBsVTcQ2fVkfBvncZ6xJWG9wtBhOldG+mHkSL0+V1K/xgZz0JDO5UT5hFwHUghg==} engines: {node: '>=10.12.0'} dependencies: - '@jridgewell/trace-mapping': 0.3.19 - '@types/istanbul-lib-coverage': 2.0.4 - convert-source-map: 1.9.0 + '@jridgewell/trace-mapping': 0.3.20 + '@types/istanbul-lib-coverage': 2.0.5 + convert-source-map: 2.0.0 dev: true - /vite-node@0.34.4(@types/node@20.6.0): - resolution: {integrity: sha512-ho8HtiLc+nsmbwZMw8SlghESEE3KxJNp04F/jPUCLVvaURwt0d+r9LxEqCX5hvrrOQ0GSyxbYr5ZfRYhQ0yVKQ==} + /vite-node@0.34.6(@types/node@20.8.10): + resolution: {integrity: sha512-nlBMJ9x6n7/Amaz6F3zJ97EBwR2FkzhBRxF5e+jE6LA3yi6Wtc2lyTij1OnDMIr34v5g/tVQtsVAzhT0jc5ygA==} engines: {node: '>=v14.18.0'} hasBin: true dependencies: @@ -2451,7 +2494,7 @@ packages: mlly: 1.4.2 pathe: 1.1.1 picocolors: 1.0.0 - vite: 4.4.9(@types/node@20.6.0) + vite: 4.5.0(@types/node@20.8.10) transitivePeerDependencies: - '@types/node' - less @@ -2463,8 +2506,8 @@ packages: - terser dev: true - /vite@4.4.9(@types/node@20.6.0): - resolution: {integrity: sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==} + /vite@4.5.0(@types/node@20.8.10): + resolution: {integrity: sha512-ulr8rNLA6rkyFAlVWw2q5YJ91v098AFQ2R0PRFwPzREXOUJQPtFUG0t+/ZikhaOCDqFoDhN6/v8Sq0o4araFAw==} engines: {node: ^14.18.0 || >=16.0.0} hasBin: true peerDependencies: @@ -2491,16 +2534,16 @@ packages: terser: optional: true dependencies: - '@types/node': 20.6.0 + '@types/node': 20.8.10 esbuild: 0.18.20 - postcss: 8.4.29 - rollup: 3.29.0 + postcss: 8.4.31 + rollup: 3.29.4 optionalDependencies: fsevents: 2.3.3 dev: true - /vitest@0.34.4: - resolution: {integrity: sha512-SE/laOsB6995QlbSE6BtkpXDeVNLJc1u2LHRG/OpnN4RsRzM3GQm4nm3PQCK5OBtrsUqnhzLdnT7se3aeNGdlw==} + /vitest@0.34.6: + resolution: {integrity: sha512-+5CALsOvbNKnS+ZHMXtuUC7nL8/7F1F2DnHGjSsszX8zCjWSSviphCb/NuS9Nzf4Q03KyyDRBAXhF/8lffME4Q==} engines: {node: '>=v14.18.0'} hasBin: true peerDependencies: @@ -2530,29 +2573,29 @@ packages: webdriverio: optional: true dependencies: - '@types/chai': 4.3.6 - '@types/chai-subset': 1.3.3 - '@types/node': 20.6.0 - '@vitest/expect': 0.34.4 - '@vitest/runner': 0.34.4 - '@vitest/snapshot': 0.34.4 - '@vitest/spy': 0.34.4 - '@vitest/utils': 0.34.4 - acorn: 8.10.0 - acorn-walk: 8.2.0 + '@types/chai': 4.3.9 + '@types/chai-subset': 1.3.4 + '@types/node': 20.8.10 + '@vitest/expect': 0.34.6 + '@vitest/runner': 0.34.6 + '@vitest/snapshot': 0.34.6 + '@vitest/spy': 0.34.6 + '@vitest/utils': 0.34.6 + acorn: 8.11.2 + acorn-walk: 8.3.0 cac: 6.7.14 - chai: 4.3.8 + chai: 4.3.10 debug: 4.3.4 local-pkg: 0.4.3 - magic-string: 0.30.3 + magic-string: 0.30.5 pathe: 1.1.1 picocolors: 1.0.0 std-env: 3.4.3 strip-literal: 1.3.0 - tinybench: 2.5.0 + tinybench: 2.5.1 tinypool: 0.7.0 - vite: 4.4.9(@types/node@20.6.0) - vite-node: 0.34.4(@types/node@20.6.0) + vite: 4.5.0(@types/node@20.8.10) + vite-node: 0.34.6(@types/node@20.8.10) why-is-node-running: 2.2.2 transitivePeerDependencies: - less @@ -2574,12 +2617,12 @@ packages: is-symbol: 1.0.4 dev: true - /which-typed-array@1.1.11: - resolution: {integrity: sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==} + /which-typed-array@1.1.13: + resolution: {integrity: sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==} engines: {node: '>= 0.4'} dependencies: available-typed-arrays: 1.0.5 - call-bind: 1.0.2 + call-bind: 1.0.5 for-each: 0.3.3 gopd: 1.0.1 has-tostringtag: 1.0.0 diff --git a/src/client.ts b/src/client.ts index 0cb612c..902f9a9 100644 --- a/src/client.ts +++ b/src/client.ts @@ -1,5 +1,7 @@ import { PrettyError } from "@infinite-fansub/logger"; +import { readFile } from "node:fs/promises"; import { createClient } from "redis"; +import { join } from "node:path"; import { Schema } from "./schema"; import { Model } from "./model"; @@ -22,40 +24,35 @@ export class Client> = new Map(); #open: boolean = false; - #prefix: string = "redis-om"; #options: ClientOptions; public constructor(options?: ClientOptions) { this.#options = options ?? >{}; - - if (this.#options.modules) { - for (let i = 0, len = this.#options.modules.length; i < len; i++) { - const module = this.#options.modules[i]; - //@ts-expect-error shenanigans - this[module.name] = new module.ctor(this); - } - } } public async connect(url: string | URLObject = this.#options.url ?? "redis://localhost:6379"): Promise { - if (this.#open) return this; + return new Promise((resolve, reject) => { + if (this.#open) resolve(this); - if (typeof url === "object") { + if (typeof url === "object") { + const { username, password, entrypoint, port } = url; + url = `redis://${username}:${password}@${(/:\d$/).exec(entrypoint) ? entrypoint : `${entrypoint}:${port}`}`; + } - const { username, password, entrypoint, port } = url; - url = `${username}:${password}@${(/:\d$/).exec(entrypoint) ? entrypoint : `${entrypoint}:${port}`}`; - } + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + this.#client ??= createClient({ url }); - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - this.#client ??= createClient({ url }); - try { - await this.#client.connect(); - this.#open = true; - } catch (e) { - Promise.reject(e); - } + this.#client.connect().then(async () => { + this.#open = true; + if (this.#options.enableInjections) { + await this.#client.functionLoad(( + await readFile(join(__dirname, "scripts/create-relation.lua")) + ).toString("utf8"), { REPLACE: true }); + } - return this; + resolve(this); + }).catch((e) => reject(e)); + }); } public async disconnect(): Promise { @@ -72,18 +69,18 @@ export class Client, M extends MethodsDefinition<(T & SD)>>(definition: T, methods?: M, options?: SchemaOptions): Schema< + public schema, M extends MethodsDefinition<(T & SD)> = {}>(definition: T, methods?: M, options?: SchemaOptions): Schema< { [K in keyof (T & SD)]: (T & SD)[K] }, { [K in keyof (M & MD)]: (M & MD)[K] } > { return new Schema({ - ...this.#options.inject?.schema?.definition, + ...this.#options.base?.schema?.definition, ...definition }, { - ...this.#options.inject?.schema?.methods, + ...this.#options.base?.schema?.methods, ...methods }, { - ...this.#options.inject?.schema?.options, + ...this.#options.base?.schema?.options, ...options }); } @@ -96,7 +93,7 @@ export class Clientmodel; } @@ -138,10 +135,6 @@ export class Client { +export function documentFieldToHASHValue(field: ParsedFieldType | { type: ParsedFieldType["type"] }, value: any, key?: string): Array { if (field.type === "boolean") return keyExists(booleanToString(value), key); if (field.type === "date") return keyExists(dateToNumber(value).toString(), key); - if (field.type === "point") return keyExists(`${value.longitude},${value.latitude}`, key); - if (field.type === "vector") return keyExists(Buffer.from(value).toString(), key); + if (field.type === "point") return keyExists(`"${value.longitude},${value.latitude}"`, key); + if (field.type === "vector") { + if (value.length === 0) return []; + return keyExists(Buffer.from(Array.isArray(value) ? new Float64Array(value).buffer : value.buffer), key); + } + if (field.type === "object") { if (!("properties" in field) || field.properties === null) return keyExists(JSON.stringify(value), key); if (!key) throw new PrettyError("Something went terribly wrong"); @@ -17,7 +21,7 @@ export function documentFieldToHASHValue(field: ParsedFieldType | { type: Parsed if (field.type === "array") { if (!("elements" in field)) return keyExists(value.toString(), key); - const temp: Array = []; + const temp: Array = []; if (typeof field.elements === "object") { for (let i = 0, length = value.length; i < length; i++) { @@ -47,8 +51,8 @@ export function documentFieldToHASHValue(field: ParsedFieldType | { type: Parsed return keyExists(value.toString(), key); } -function flatten(field: ParsedSchemaDefinition["data"], value: any, key?: string): Array { - const temp: Array = []; +function flatten(field: ParsedSchemaDefinition["data"], value: any, key?: string): Array { + const temp: Array = []; for (let i = 0, entries = Object.entries(field), length = entries.length; i < length; i++) { const [k, val] = entries[i]; @@ -59,7 +63,7 @@ function flatten(field: ParsedSchemaDefinition["data"], value: any, key?: string return temp; } -function keyExists(value: string, key: string | undefined): Array { +function keyExists(value: string | Buffer, key: string | undefined): Array { return key ? [key, value] : [value]; } @@ -79,8 +83,9 @@ export function HASHValueToDocumentField( } if (field.type === "vector") { - if (!("vecType" in field) || field.vecType === "FLOAT32") return new Float32Array(Buffer.from(value)); - return new Float64Array(Buffer.from(value)); + const buff = Buffer.from(value, "latin1"); + if (!("vecType" in field) || field.vecType === "FLOAT32") return new Float32Array(buff.buffer, buff.byteOffset, buff.byteLength / Float32Array.BYTES_PER_ELEMENT); + return new Float64Array(buff.buffer, buff.byteOffset, buff.byteLength / Float64Array.BYTES_PER_ELEMENT); } if (field.type === "object") { diff --git a/src/document/document-helpers/index.ts b/src/document/document-helpers/index.ts index 8559e4e..925b1a8 100644 --- a/src/document/document-helpers/index.ts +++ b/src/document/document-helpers/index.ts @@ -1,3 +1,3 @@ +export * from "./general-helpers"; export * from "./hash-helpers"; -export * from "./json-helpers"; -export * from "./general-helpers"; \ No newline at end of file +export * from "./json-helpers"; \ No newline at end of file diff --git a/src/document/document-helpers/json-helpers.ts b/src/document/document-helpers/json-helpers.ts index 4b285f0..988bba3 100644 --- a/src/document/document-helpers/json-helpers.ts +++ b/src/document/document-helpers/json-helpers.ts @@ -6,7 +6,11 @@ export function documentFieldToJSONValue(field: ParsedFieldType | { type: Parsed if (field.type === "bigint") return value.toString(); if (field.type === "date") return dateToNumber(value); if (field.type === "point") return `${value.longitude},${value.latitude}`; - if (field.type === "vector") return Array.from(value); + if (field.type === "vector") { + if (value.length === 0) return undefined; + return Array.from(value); + } + if (field.type === "object") { if (!("properties" in field) || field.properties === null) return value; return transformParsedDefinition(field.properties, value, documentFieldToJSONValue); diff --git a/src/document/hash-document.ts b/src/document/hash-document.ts index e1b7ec5..429c85e 100644 --- a/src/document/hash-document.ts +++ b/src/document/hash-document.ts @@ -1,4 +1,5 @@ /* eslint-disable @typescript-eslint/naming-convention */ +import { PrettyError } from "@infinite-fansub/logger"; import { randomUUID } from "node:crypto"; import { ReferenceArray } from "../utils"; @@ -10,7 +11,6 @@ import { } from "./document-helpers"; import type { DocumentShared, ParsedSchemaDefinition } from "../typings"; -import { PrettyError } from "@infinite-fansub/logger"; export class HASHDocument implements DocumentShared { @@ -40,7 +40,7 @@ export class HASHDocument implements DocumentShared { globalPrefix: string, prefix: string, name: string, - suffix?: string | (() => string) | undefined, + suffix: string | (() => string) | undefined, id?: string | undefined }, data?: Record, @@ -145,10 +145,10 @@ export class HASHDocument implements DocumentShared { : value.type === "tuple" || value.type === "array" ? [] : value.type === "vector" - ? value.vecType === "FLOAT32" - ? new Float32Array() - : new Float64Array() - : void 0); + ? value.vecType === "FLOAT64" + ? new Float64Array() + : new Float32Array() + : undefined); } for (let i = 0, keys = Object.keys(this.#schema.references), len = keys.length; i < len; i++) { @@ -157,8 +157,7 @@ export class HASHDocument implements DocumentShared { } } - /** This is actually and array... eventually i change it */ - public toString(): Array { + public toString(): string { if (this.#validate) this.#validateSchemaData(this.#schema.data, this); const arr = [ @@ -170,14 +169,17 @@ export class HASHDocument implements DocumentShared { for (let i = 0, entries = Object.entries(this), length = entries.length; i < length; i++) { const [key, val] = entries[i]; + if (key.startsWith("$") || key.startsWith("_")) continue; + const schema = this.#schema.data[key]; if (typeof schema !== "undefined") { + //@ts-expect-error This can return a Buffer but it has to be like this until i find out a better way to pass in buffers arr.push(...documentFieldToHASHValue(schema, val, key)); continue; } - if (typeof this.#schema.references[key] === "undefined") arr.push(val); + if (typeof this.#schema.references[key] === "undefined" && typeof this.#schema.relations[key] === "undefined") arr.push(val); } if (!this.#autoFetch) { @@ -189,7 +191,7 @@ export class HASHDocument implements DocumentShared { } } - return arr; + return arr.join(" "); } public get $globalPrefix(): string { @@ -200,7 +202,7 @@ export class HASHDocument implements DocumentShared { return this.#prefix; } - public get $model_name(): string { + public get $modelName(): string { return this.#model_name; } @@ -212,7 +214,7 @@ export class HASHDocument implements DocumentShared { return this.#id; } - public get $record_id(): string { + public get $recordId(): string { return this.#record_id; } } \ No newline at end of file diff --git a/src/document/json-document.ts b/src/document/json-document.ts index 4c3c019..76d6898 100644 --- a/src/document/json-document.ts +++ b/src/document/json-document.ts @@ -39,7 +39,7 @@ export class JSONDocument implements DocumentShared { globalPrefix: string, prefix: string, name: string, - suffix?: string | (() => string) | undefined, + suffix: string | (() => string) | undefined, id?: string | undefined }, data?: Record, @@ -96,10 +96,10 @@ export class JSONDocument implements DocumentShared { : value.type === "tuple" || value.type === "array" ? [] : value.type === "vector" - ? value.vecType === "FLOAT32" - ? new Float32Array() - : new Float64Array() - : void 0); + ? value.vecType === "FLOAT64" + ? new Float64Array() + : new Float32Array() + : undefined); } for (let i = 0, keys = Object.keys(this.#schema.references), len = keys.length; i < len; i++) { @@ -118,6 +118,8 @@ export class JSONDocument implements DocumentShared { for (let i = 0, entries = Object.entries(this), length = entries.length; i < length; i++) { const [key, val] = entries[i]; + if (key.startsWith("$") || key.startsWith("_")) continue; + const schema = this.#schema.data[key]; if (typeof schema !== "undefined") { @@ -125,8 +127,7 @@ export class JSONDocument implements DocumentShared { continue; } - if (typeof this.#schema.references[key] === "undefined") obj[key] = val; - + if (typeof this.#schema.references[key] === "undefined" && typeof this.#schema.relations[key] === "undefined") obj[key] = val; } if (!this.#autoFetch) { @@ -148,7 +149,7 @@ export class JSONDocument implements DocumentShared { return this.#prefix; } - public get $model_name(): string { + public get $modelName(): string { return this.#model_name; } @@ -160,7 +161,7 @@ export class JSONDocument implements DocumentShared { return this.#id; } - public get $record_id(): string { + public get $recordId(): string { return this.#record_id; } } \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index e06f048..b2c8ce6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,10 @@ +export type * from "./search/search"; +export type * from "./document"; +export type * from "./schema"; export type * from "./model"; -export * from "./client"; -export * from "./schema"; \ No newline at end of file + +/* The library exposes all its types but please use them only if you know what you are doing */ +export type * from "./typings"; + +export * from "./utils/reference-array"; +export * from "./client"; \ No newline at end of file diff --git a/src/model.ts b/src/model.ts index 10cd9f0..efb5348 100644 --- a/src/model.ts +++ b/src/model.ts @@ -1,17 +1,21 @@ import { PrettyError } from "@infinite-fansub/logger"; import { createHash } from "node:crypto"; +import { parseRelationsToSearchIndex, parseSchemaToSearchIndex } from "./utils"; import { JSONDocument, HASHDocument } from "./document"; import { methods, schemaData } from "./utils/symbols"; -import { parseSchemaToSearchIndex } from "./utils"; +import { Relation } from "./relation/relation"; import { Search } from "./search/search"; import type { Schema } from "./schema"; import type { ExtractParsedSchemaDefinition, - ReturnDocument, + ParsedRelationsToSearch, + SchemaDefinition, NodeRedisClient, + ReturnDocument, ModelOptions, + ParseSchema, ParsedMap, MapSchema, Document @@ -20,56 +24,61 @@ import type { export class Model> { readonly #schema: S; readonly #client: NodeRedisClient; - readonly #searchIndexName: string; - readonly #searchIndexHashName: string; - readonly #searchIndexBase: Array; - readonly #searchIndex: Array; - readonly #searchIndexHash: string; readonly #parsedSchema: ParsedMap; - // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents readonly #docType: typeof JSONDocument | typeof HASHDocument; + readonly #relationsToIndex: ParsedRelationsToSearch; + readonly #searchIndex: { + name: string, + hash: string, + query: Array + } = {}; #options: ModelOptions; - public constructor(client: NodeRedisClient, globalPrefix: string, ver: string, private readonly name: string, data: S) { + public constructor( + client: NodeRedisClient, + globalPrefix: string, + prefix: string, + private readonly name: string, + injectScripts: boolean, + data: S + ) { this.#client = client; this.#schema = data; this.#options = { - ...this.#schema.options, + injectScripts, skipDocumentValidation: !this.#schema.options.skipDocumentValidation, globalPrefix, - prefix: this.#schema.options.prefix ?? ver, - version: ver + prefix: this.#schema.options.prefix ?? prefix, + suffix: this.#schema.options.suffix }; - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const { map, index } = parseSchemaToSearchIndex(this.#schema[schemaData].data, this.#options.dataStructure!); + const { map, index } = parseSchemaToSearchIndex(this.#schema[schemaData].data, this.#schema.options.dataStructure); + this.#relationsToIndex = parseRelationsToSearchIndex(this.#schema[schemaData].relations, this.#schema.options.dataStructure, `${globalPrefix}:${this.#options.prefix}:${this.name}`); this.#parsedSchema = map; - this.#searchIndexName = `${globalPrefix}:${this.#options.prefix}:${this.name}:index`; - this.#searchIndexHashName = `${globalPrefix}:${this.#options.prefix}:${this.name}:index:hash`; - this.#searchIndexBase = [ + this.#searchIndex.name = `${globalPrefix}:${this.#options.prefix}:${this.name}:index`; + this.#searchIndex.query = [ "FT.CREATE", - this.#searchIndexName, + this.#searchIndex.name, "ON", - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - data.options.dataStructure!, + data.options.dataStructure, "PREFIX", "1", `${globalPrefix}:${this.#options.prefix}:${this.name}:` ]; - if (this.#options.language) this.#searchIndexBase.push("LANGUAGE", this.#options.language); - if (this.#options.stopWords) { - this.#searchIndexBase.push("STOPWORDS", this.#options.stopWords.length.toString()); - if (this.#options.stopWords.length > 0) this.#searchIndexBase.push(...this.#options.stopWords); + if (this.#schema.options.language) this.#searchIndex.query.push("LANGUAGE", this.#schema.options.language); + if (this.#schema.options.stopWords) { + this.#searchIndex.query.push("STOPWORDS", this.#schema.options.stopWords.length.toString()); + if (this.#schema.options.stopWords.length > 0) this.#searchIndex.query.push(...this.#schema.options.stopWords); } - this.#searchIndexBase.push("SCHEMA"); + this.#searchIndex.query.push("SCHEMA"); + this.#searchIndex.query.push(...index); - this.#searchIndex = index; - this.#searchIndexHash = createHash("sha1").update(JSON.stringify({ + this.#searchIndex.hash = createHash("sha1").update(JSON.stringify({ name, - structure: this.#options.dataStructure, + structure: this.#schema.options.dataStructure, definition: this.#schema[schemaData].data })).digest("base64"); @@ -79,30 +88,37 @@ export class Model> { else this.#docType = JSONDocument; } - public async get(id: string | number, autoFetch?: F): Promise | undefined> { + public async get< + FREF extends boolean, + FREL extends boolean, + K extends keyof T, + T extends ExtractParsedSchemaDefinition["relations"] = ExtractParsedSchemaDefinition["relations"], + MOR extends boolean = false + >( + id: string | number, + options?: { + withReferences?: FREF, + withRelations?: FREL, + + /** This only works if you are using `relationsConstrain` */ + returnMetadataOverRelation?: MOR, + relationsConstrain?: Record>) => Search>> + } + ): Promise | undefined> { if (typeof id === "undefined") throw new PrettyError("A valid id was not given", { reference: "redis-om" }); - if (id.toString().split(":").length === 1) { - const suffix = this.#options.suffix; - if (typeof suffix === "function") { - throw new PrettyError("Due to the use of dynamic suffixes you gave to pass in a full id", { - reference: "redis-om" - }); - } - // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions - id = `${this.#options.globalPrefix}:${this.#options.prefix}:${this.name}:${suffix ? `${suffix}:` : ""}${id}`; - } + id = this.formatId(id.toString()); - const data = this.#options.dataStructure === "JSON" ? await this.#client.json.get(id.toString()) : await this.#client.hGetAll(id.toString()); + const data = this.#schema.options.dataStructure === "JSON" ? await this.#client.json.get(id.toString()) : await this.#client.hGetAll(id.toString()); - if (data === null) return void 0; - if (autoFetch) { + if (data === null || Object.keys(data).length === 0) return undefined; + if (options?.withReferences) { for (let i = 0, keys = Object.keys(this.#schema[schemaData].references), len = keys.length; i < len; i++) { const key = keys[i]; //@ts-expect-error node-redis types decided to die - const val = this.#options.dataStructure === "JSON" ? data[key] : data[key].split(" | "); + const val = this.#schema.options.dataStructure === "JSON" ? data[key] : data[key].split(" | "); const temp = []; for (let j = 0, le = val.length; j < le; j++) { @@ -114,16 +130,109 @@ export class Model> { } } + if (options?.withRelations) { + if (!this.#options.injectScripts) throw new PrettyError("Cannot get relations without the required scripts", { reference: "redis-om" }); + if (typeof options.relationsConstrain !== "undefined") { + const tempConstrains: Record = {}; + + for (let i = 0, entries = Object.entries(options.relationsConstrain), length = entries.length; i < length; i++) { + const [key, value] = <[string, (s: Search>) => Search>]>entries[i]; + + tempConstrains[key] = [ + `${this.#relationsToIndex[key].key}:index`, + value(new Search(this.#client, { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + data: this.#schema[schemaData].relations[key].meta!, + references: {}, + relations: {} + }, this.#docType, this.#relationsToIndex[key].data.map, { + ...this.#options, + modelName: this.name, + suffix: this.#options.suffix, + searchIndex: `${this.#relationsToIndex[key].key}:index`, + dataStructure: this.#schema.options.dataStructure + })).rawQuery + ]; + } + + const fetched: Record> = JSON.parse(await this.#client.sendCommand([ + "FCALL", + this.#schema.options.dataStructure === "JSON" ? "JSONSR" : "HSR", + "1", + JSON.stringify(tempConstrains), + (+(options.returnMetadataOverRelation ?? false)).toString() + ])); + + for (let i = 0, entries = Object.entries(fetched), length = entries.length; i < length; i++) { + const [key, value] = entries[i]; + + for (let j = 0, len = value.length; j < len; j++) { + value[j] = new this.#docType({ + data: (options.returnMetadataOverRelation + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + ? this.#schema[schemaData].relations[key].meta! + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + : this.#schema[schemaData].relations[key].schema ?? this.#schema[schemaData].data), + references: {}, + relations: {} + }, { + globalPrefix: this.#options.globalPrefix, + prefix: this.#options.prefix, + name: this.name, + suffix: this.#options.suffix + }, value[j], true, this.#options.skipDocumentValidation); + } + + //@ts-expect-error node-redis types decided to die + data[key] = value; + } + } else { + for (let i = 0, entries = Object.entries(this.#schema[schemaData].relations), length = entries.length; i < length; i++) { + const [key, value] = entries[i]; + + const arr: Array> = JSON.parse(await this.#client.sendCommand([ + "FCALL", + this.#schema.options.dataStructure === "JSON" ? "JSONGR" : "HGR", + "1", + id, + key, + (+(options.returnMetadataOverRelation ?? false)).toString() + ])); + + for (let j = 0, len = arr.length; j < len; j++) { + arr[j] = new this.#docType({ + data: (options.returnMetadataOverRelation + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + ? value.meta! + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + : value.schema ?? this.#schema[schemaData].data), + references: {}, + relations: {} + }, { + globalPrefix: this.#options.globalPrefix, + prefix: this.#options.prefix, + name: this.name, + suffix: this.#options.suffix + }, arr[j], true, this.#options.skipDocumentValidation); + } + //@ts-expect-error node-redis types decided to die + data[key] = arr; + } + } + } + return new this.#docType(this.#schema[schemaData], { globalPrefix: this.#options.globalPrefix, prefix: this.#options.prefix, - name: this.name - }, data, true, this.#options.skipDocumentValidation, autoFetch); + name: this.name, + suffix: this.#options.suffix + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + }, data, true, this.#options.skipDocumentValidation, options?.withRelations || options?.withReferences); } public create(id?: string | number): ReturnDocument; - public create(data?: { $id?: string | number } & MapSchema, true, true>): ReturnDocument; - public create(idOrData?: string | number | { $id?: string | number } & MapSchema, true, true>): ReturnDocument { + public create(data?: { $id?: string | number } & MapSchema, true, true, false, true>): ReturnDocument; + public create(idOrData?: string | number | { $id?: string | number } & MapSchema, true, true, false, true>): ReturnDocument { if (typeof idOrData === "object") { return new this.#docType(this.#schema[schemaData], { globalPrefix: this.#options.globalPrefix, @@ -139,7 +248,7 @@ export class Model> { name: this.name, suffix: this.#options.suffix, id: idOrData?.toString() - }, void 0, false, this.#options.skipDocumentValidation, false); + }, undefined, false, this.#options.skipDocumentValidation, false); } public async save(doc: Document): Promise { @@ -147,31 +256,29 @@ export class Model> { reference: "redis-om" }); - // eslint-disable-next-line @typescript-eslint/no-base-to-string - if (this.#options.dataStructure === "HASH") await this.#client.sendCommand(["HSET", doc.$record_id, ...doc.toString()]); - // eslint-disable-next-line @typescript-eslint/no-base-to-string - else await this.#client.sendCommand(["JSON.SET", doc.$record_id, "$", doc.toString()]); + if (this.#schema.options.dataStructure === "HASH") await this.#client.sendCommand(["HSET", doc.$recordId, ...doc.toString()]); + else await this.#client.sendCommand(["JSON.SET", doc.$recordId, "$", doc.toString()]); } public async delete(...docs: Array): Promise { if (!docs.length) throw new PrettyError("No documents were given to delete", { reference: "redis-om" }); - await this.#client.del(this.#stringOrDocToString(docs)); + await this.#client.del(this.#idsOrDocsToString(docs)); } public async exists(...docs: Array): Promise { if (!docs.length) throw new PrettyError("No documents were given to check", { reference: "redis-om" }); - return await this.#client.exists(this.#stringOrDocToString(docs)); + return await this.#client.exists(this.#idsOrDocsToString(docs)); } public async expire(docs: Array, seconds: number | Date, mode?: "NX" | "XX" | "GT" | "LT"): Promise { if (!docs.length) throw new PrettyError("No documents were given to expire", { reference: "redis-om" }); - docs = this.#stringOrDocToString(docs); + docs = this.#idsOrDocsToString(docs); if (seconds instanceof Date) seconds = Math.round((seconds.getTime() - Date.now()) / 1000); @@ -185,86 +292,132 @@ export class Model> { await Promise.all(temp); } - public async createAndSave(data: { $id?: string | number } & MapSchema, true, true>): Promise { - const doc = new this.#docType(this.#schema[schemaData], { - globalPrefix: this.#options.globalPrefix, - prefix: this.#options.prefix, - name: this.name, - suffix: this.#options.suffix - }, data, false, this.#options.skipDocumentValidation, false); - await this.save(doc); + public async createAndSave(id?: string | number): Promise; + public async createAndSave(data?: { $id?: string | number } & MapSchema, true, true, false, true>): Promise; + public async createAndSave(idOrData?: string | number | { $id?: string | number } & MapSchema, true, true, false, true>): Promise { + await this.save(this.create(idOrData)); } public search(): Search> { - // eslint-disable-next-line max-len - return new Search>(this.#client, this.#schema[schemaData], this.#parsedSchema, { + return new Search>(this.#client, this.#schema[schemaData], this.#docType, this.#parsedSchema, { + ...this.#options, + modelName: this.name, + suffix: this.#options.suffix, + searchIndex: this.#searchIndex.name, + dataStructure: this.#schema.options.dataStructure + }); + } + + public relate(idOrDoc: string | number | Document): Relation> { + return new Relation(this.#client, this.#idOrDocToString(idOrDoc), { ...this.#options, modelName: this.name, - searchIndex: this.#searchIndexName + suffix: this.#options.suffix, + dataStructure: this.#schema.options.dataStructure }); } public async createIndex(): Promise { - const currentIndexHash = await this.#client.get(this.#searchIndexHashName); - if (currentIndexHash === this.#searchIndexHash) return; + if (!this.#options.injectScripts) { + if (!this.#schema.options.noLogs) console.warn("Cannot index relations... Skipping"); + } else { + for (let i = 0, values = Object.values(this.#relationsToIndex), length = values.length; i < length; i++) { + const value = values[i]; + + if (await this.#client.get(`${value.key}:index:hash`) === value.hash) continue; + + try { + await Promise.all([ + this.#client.unlink(`${value.key}:index:hash`), + this.#client.ft.dropIndex(`${value.key}:index`), + this.#client.set(`${value.key}:index:hash`, value.hash), + this.#client.sendCommand([ + "FT.CREATE", + `${value.key}:index`, + "ON", + this.#schema.options.dataStructure, + "PREFIX", + "1", + `${value.key}:`, + "SCHEMA", + ...value.data.index + ]) + ]); + } catch (e) { + if (e instanceof Error && e.message === "Unknown Index name") { + continue; + } else throw e; + } - if (this.#searchIndex.length === 0) { - if (!this.#options.noLogs) console.log("Nothing to index... Skipping"); + } + } + + if (await this.#client.get(`${this.#searchIndex.name}:hash`) === this.#searchIndex.hash) return; + + if (this.#searchIndex.query.at(-1) === "SCHEMA") { + if (!this.#schema.options.noLogs) console.log("Nothing to index... Skipping"); return; } await this.deleteIndex(); await Promise.all([ - this.#client.set(this.#searchIndexHashName, this.#searchIndexHash), - this.#client.sendCommand([...this.#searchIndexBase, ...this.#searchIndex]) + this.#client.set(`${this.#searchIndex.name}:hash`, this.#searchIndex.hash), + this.#client.sendCommand(this.#searchIndex.query) ]); } public async deleteIndex(): Promise { try { await Promise.all([ - this.#client.unlink(this.#searchIndexHashName), - this.#client.ft.dropIndex(this.#searchIndexName) + this.#client.unlink(`${this.#searchIndex.name}:hash`), + this.#client.ft.dropIndex(this.#searchIndex.name) ]); } catch (e) { if (e instanceof Error && e.message === "Unknown Index name") { - Promise.resolve(); + return; } else throw e; } } public async rawSearch(...args: Array): Promise> { - return await this.#client.ft.search(this.#searchIndexName, args.join(" ")); + return await this.#client.ft.search(this.#searchIndex.name, args.join(" ")); } - #stringOrDocToString(stringOrNumOrDoc: Array): Array { - const temp = []; - - for (let i = 0, len = stringOrNumOrDoc.length; i < len; i++) { - const el = stringOrNumOrDoc[i]; - let id = ""; + public sanitize(string: string): string { + return string.replaceAll(/[,.?<>{}[\]"':;!@#$%^&()\-+=~|/\\ ]/g, "\\$&"); + } - if (el instanceof JSONDocument || el instanceof HASHDocument) { - id = el.$record_id; - } else if (el.toString().split(":").length === 1) { - const suffix = this.#options.suffix; + public formatId(id: string): string { + if (id.split(":").length === 1) { + const suffix = this.#options.suffix; - if (typeof suffix === "function") { - throw new PrettyError("Due to the use of dynamic suffixes you gave to pass in a full id", { - reference: "redis-om" - }); - } - // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions - id = `${this.#options.globalPrefix}:${this.#options.prefix}:${this.name}:${suffix ? `${suffix}:` : ""}${el.toString()}`; + if (typeof suffix === "function") { + throw new PrettyError("Due to the use of dynamic suffixes you gave to pass in a full id", { + reference: "redis-om" + }); } - temp.push(id); + return `${this.#options.globalPrefix}:${this.#options.prefix}:${this.name}:${suffix ? `${suffix}:` : ""}${id}`; + } + + return id; + } + + #idsOrDocsToString(idsOrDocs: Array): Array { + const temp = []; + + for (let i = 0, len = idsOrDocs.length; i < len; i++) { + temp.push(this.#idOrDocToString(idsOrDocs[i])); } return temp; } + #idOrDocToString(idOrDoc: string | number | Document): string { + return idOrDoc instanceof JSONDocument || idOrDoc instanceof HASHDocument ? idOrDoc.$recordId : this.formatId(idOrDoc.toString()); + } + #defineMethods(): void { for (let i = 0, entries = Object.entries(this.#schema[methods]), len = entries.length; i < len; i++) { const [key, value] = entries[i]; @@ -277,7 +430,8 @@ export class Model> { return this.#options; } - public set options(options: Partial) { + public set options(options: Partial>) { + if ("globalPrefix" in options) throw new PrettyError("To edit the global prefix please use the client options", { reference: "redis-om" }); this.#options = { ...this.#options, ...options }; } } \ No newline at end of file diff --git a/src/relation/relation.ts b/src/relation/relation.ts new file mode 100644 index 0000000..bf357e3 --- /dev/null +++ b/src/relation/relation.ts @@ -0,0 +1,89 @@ +import { PrettyError } from "@infinite-fansub/logger"; +import { randomUUID } from "crypto"; + +import { JSONDocument, HASHDocument } from "../document"; + +import type { + ModelInformation, + NodeRedisClient, + ParseSchemaData, + MapSchemaData, + ParseSchema, + Document +} from "../typings"; + +export class Relation, F extends ParseSchema["relations"][number]["meta"] = {}> { + readonly #client: NodeRedisClient; + readonly #information: ModelInformation; + readonly #in: string; + + #out?: string; + #field?: string; + #meta?: T; + + public constructor( + client: NodeRedisClient, + id: string, + information: ModelInformation + ) { + this.#client = client; + this.#information = information; + this.#in = id; + } + + public to(id: string | number | Document): this { + if (id instanceof JSONDocument || id instanceof HASHDocument) id = id.$recordId; + else if (id.toString().split(":").length === 1) { + const suffix = this.#information.suffix; + + if (typeof suffix === "function") { + throw new PrettyError("Due to the use of dynamic suffixes you gave to pass in a full id", { + reference: "redis-om" + }); + } + + id = `${this.#information.globalPrefix}:${this.#information.prefix}:${this.#information.modelName}:${suffix ? `${suffix}:` : ""}${id}`; + } + + this.#out = id.toString(); + + return this; + } + + public as(field: K): Relation { + this.#field = field; + return this; + } + + public with(meta: [keyof F] extends [never] ? never : MapSchemaData & ParseSchemaData>): this { + this.#meta = meta; + return this; + } + + public async exec(): Promise { + if (typeof this.#out === "undefined" || typeof this.#field === "undefined") throw new PrettyError("Relation query was not properly formatted", { + reference: "redis-om" + }); + + const arr = ["FCALL"]; + const omitId = `${this.#information.globalPrefix}:${this.#information.prefix}:${this.#information.modelName}-relation-${this.#field}:${randomUUID()}`; + + if (this.#information.dataStructure === "JSON") { + arr.push("JSONCR"); + } else { + arr.push("HCR"); + } + + arr.push( + "3", + this.#in, + this.#out, + omitId, + this.#field + ); + + if (typeof this.#meta !== "undefined") arr.push(JSON.stringify(this.#meta)); + + await this.#client.sendCommand(arr); + } +} \ No newline at end of file diff --git a/src/schema.ts b/src/schema.ts index c9a46af..8449bbe 100644 --- a/src/schema.ts +++ b/src/schema.ts @@ -6,6 +6,7 @@ import { methods, schemaData } from "./utils/symbols"; import type { ExtractSchemaDefinition, + ParsedSchemaDefinition, MethodsDefinition, SchemaDefinition, SchemaOptions, @@ -13,11 +14,12 @@ import type { NumberField, StringField, BaseField, - FieldType, - ParsedSchemaDefinition + FieldType } from "./typings"; -export class Schema = MethodsDefinition, P extends ParseSchema = ParseSchema> { +export class Schema = {}, P extends ParseSchema = ParseSchema> { + + public readonly options: SchemaOptions & ({ dataStructure: (SchemaOptions["dataStructure"] & {}) }); /** @internal */ public [methods]: M; @@ -28,9 +30,10 @@ export class Schema = */ public [schemaData]: P; - public constructor(rawData: S, methodsData?: M, public readonly options: SchemaOptions = {}) { + public constructor(rawData: S, methodsData?: M, options: SchemaOptions = {}) { this[schemaData] = this.#parse(rawData); this[methods] = methodsData ?? {}; + this.options = options; this.options.dataStructure = options.dataStructure ?? "JSON"; } @@ -50,9 +53,10 @@ export class Schema = return this; } - #parse(schema: SchemaDefinition): ParsedSchemaDefinition { + #parse(schema: SchemaDefinition, topLevelIndex: boolean = false): ParsedSchemaDefinition { const data: Record = {}; - const references: Record = {}; + const references: Record = {}; + const relations: ParsedSchemaDefinition["relations"] = {}; for (let i = 0, entries = Object.entries(schema), len = entries.length; i < len; i++) { let [key, value] = entries[i]; @@ -114,11 +118,34 @@ export class Schema = ] }); //@ts-expect-error Some people do not read docs + } else if (value === "relation") { + throw new PrettyError("Type 'relation' needs to use its object definition", { + reference: "redis-om", + lines: [ + { + error: inspect({ [key]: value }, { colors: true }), + marker: { text: "Parsing:" } + }, + { + error: inspect({ + [key]: { + type: "relation", + schema: "`SchemaInstance`" + } + }, { colors: true, compact: false }), + marker: { text: "Fix:", color: Color.fromHex("#00FF00"), newLine: true } + } + ] + }); + //@ts-expect-error Some people do not read docs } else if (value === "tuple") { throw new PrettyError("Type 'tuple' needs to use its object definition"); } else if (value === "array") { - value = { type: value, elements: "string", default: undefined, optional: false, sortable: false, index: false, separator: "|" }; + value = { type: value, elements: "string", default: undefined, optional: false, sortable: false, index: topLevelIndex, separator: "|" }; } else if (value === "vector") { + if (this.options.dataStructure === "HASH") { + throw new PrettyError("Vectors currently aren't working with hashes", { reference: "redis-om" }); + } value = { type: value, algorithm: "FLAT", @@ -128,11 +155,11 @@ export class Schema = default: undefined, optional: false, sortable: false, - index: false + index: topLevelIndex }; } else { - value = { type: value, default: undefined, optional: false, sortable: false, index: false }; - if ((value).type === "string" || (value).type === "number") (value).literal = undefined; + value = { type: value, default: undefined, optional: false, sortable: false, index: topLevelIndex }; + if ((value).type === "string" || (value).type === "number" || (value).type === "bigint") (value).literal = undefined; } data[key] = value; @@ -154,30 +181,34 @@ export class Schema = if (typeof value.elements === "undefined") value.elements = "string"; if (typeof value.separator === "undefined") value.separator = "|"; if (typeof value.elements === "object") { - value.elements = this.#parse(value.elements).data; + value.elements = this.#parse(value.elements, value.index ?? topLevelIndex).data; } - value = this.#fill(value); + value = this.#fill(value, value.index ?? topLevelIndex); } else if (value.type === "date") { if (value.default instanceof Date) value.default = value.default.getTime(); if (typeof value.default === "string" || typeof value.default === "number") value.default = new Date(value.default).getTime(); - value = this.#fill(value); + value = this.#fill(value, value.index ?? topLevelIndex); } else if (value.type === "object") { if (typeof value.default === "undefined") value.default = undefined; if (typeof value.optional === "undefined") value.optional = false; - if (typeof value.properties === "undefined") value.properties = null; - else if (value.properties instanceof Schema) value.properties = value.properties[schemaData].data; - else value.properties = this.#parse(value.properties).data; + if (typeof value.properties !== "undefined") { + if (value.properties instanceof Schema) value.properties = value.properties[schemaData].data; + else value.properties = this.#parse(value.properties, value.index ?? topLevelIndex).data; + } else { + value.properties = undefined; + } + value = this.#fill(value, value.index ?? topLevelIndex); } else if (value.type === "tuple") { if (typeof value.elements === "undefined") throw new PrettyError("Tuple needs to have at least 1 element", { reference: "redis-om" }); for (let j = 0, le = value.elements.length; j < le; j++) { - value.elements[j] = this.#parse({ [j]: value.elements[j] }).data[j]; + value.elements[j] = this.#parse({ [j]: value.elements[j] }, value.index ?? topLevelIndex).data[j]; } - value = this.#fill(value); + value = this.#fill(value, value.index ?? topLevelIndex); } else if (value.type === "reference") { // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition, @typescript-eslint/strict-boolean-expressions - if (!value.schema) throw new PrettyError("Type 'reference' lacks a schema", { + if (!value.schema) throw new PrettyError("Type 'reference' lacks a schema which is needed to provide intellisense", { reference: "redis-om", lines: [ { @@ -197,7 +228,44 @@ export class Schema = }); references[key] = null; continue; + } else if (value.type === "relation") { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition, @typescript-eslint/strict-boolean-expressions + if (!value.schema) throw new PrettyError("Type 'relation' lacks a schema", { + reference: "redis-om", + lines: [ + { + error: inspect({ [key]: value }, { colors: true }), + marker: { text: "Parsing:" } + }, + { + error: inspect({ + [key]: { + type: "relation", + schema: "`SchemaInstance`" + } + }, { colors: true, compact: false }), + marker: { text: "Fix:", color: Color.fromHex("#00FF00"), newLine: true } + } + ] + }); + + if (typeof value.meta !== "undefined") { + if (value.meta instanceof Schema) value.meta = { ...value.meta[schemaData].data, ...this.#parse({ in: "string", out: "string" }, true).data }; + else value.meta = this.#parse({ ...value.meta, in: "string", out: "string" }, true).data; + } else { + value.meta = this.#parse({ in: "string", out: "string" }, true).data; + } + + if (value.schema instanceof Schema) value.schema = value.schema[schemaData].data; + else if (value.schema === "self") value.schema = null; + else value.schema = this.#parse(value.schema, value.index ?? topLevelIndex).data; + + relations[key] = { index: value.index ?? false, schema: value.schema, meta: value.meta }; + continue; } else if (value.type === "vector") { + if (this.options.dataStructure === "HASH") { + throw new PrettyError("Vectors currently aren't working with hashes", { reference: "redis-om" }); + } if (typeof value.algorithm === "undefined") { throw new PrettyError("'algorithm' is missing on the vector definition", { reference: "redis-om" @@ -228,25 +296,25 @@ export class Schema = if (typeof value.runtime === "undefined") value.runtime = undefined; if (typeof value.epsilon === "undefined") value.epsilon = undefined; } - value = this.#fill(value); + value = this.#fill(value, value.index ?? topLevelIndex); } else if (value.type === "string" || value.type === "number" || value.type === "bigint") { if (typeof value.literal === "undefined") value.literal = undefined; else if (!Array.isArray(value.literal)) value.literal = [value.literal]; - value = this.#fill(value); + value = this.#fill(value, value.index ?? topLevelIndex); } else { - value = this.#fill(value); + value = this.#fill(value, value.index ?? topLevelIndex); } data[key] = value; } - return { data, references }; + return { data, references, relations }; } - #fill(value: BaseField): any { + #fill(value: BaseField, topLevelIndex: boolean): any { if (typeof value.default === "undefined") value.default = undefined; if (typeof value.optional === "undefined") value.optional = false; if (typeof value.sortable === "undefined") value.sortable = false; - if (typeof value.index === "undefined") value.index = false; + if (typeof value.index === "undefined") value.index = topLevelIndex; return value; } } \ No newline at end of file diff --git a/src/scripts/create-relation.lua b/src/scripts/create-relation.lua new file mode 100644 index 0000000..337a26e --- /dev/null +++ b/src/scripts/create-relation.lua @@ -0,0 +1,185 @@ +#!lua name=nekdis_create_relation + +local function hashToJson(hash) + local temp = {} + + for i = 1, #hash, 2 do + temp[hash[i]] = hash[i + 1] + end + + return temp +end + +--[[ + Recieves 3 required keys + KEYS[1] = The 'in' id + KEYS[2] = The 'out' id + KEYS[3] = The ommited id + + It recieves 2 args the second one being optional + ARGS[1] = Field name + ARGS[2] = meta to be saved along the way +]] +local function createJSONRelation(KEYS, ARGS) + local inId = KEYS[1] + local outId = KEYS[2] + local omitId = KEYS[3] + + local meta = {} + + if ARGS[2] ~= nil then + meta = cjson.decode(ARGS[2]) + end + + meta["in"] = inId + meta["out"] = outId + + -- Create the "omitted" document + redis.call("JSON.SET", omitId, "$", cjson.encode(meta)) + + -- Append to the existing array of relations + redis.call("SADD", inId .. ":" .. ARGS[1], omitId) + + return "OK" +end + +local function createHASHRelation(KEYS, ARGS) + local inId = KEYS[1] + local outId = KEYS[2] + local omitId = KEYS[3] + + local meta = {} + meta[1] = "in" + meta[2] = inId + meta[3] = "out" + meta[4] = outId + + if ARGS[2] ~= nil then + for key, value in pairs(cjson.decode(ARGS[2])) do + table.insert(meta, key) + table.insert(meta, value) + end + end + + -- Create the "omitted" document + ---@diagnostic disable-next-line: deprecated + redis.call("HSET", omitId, unpack(meta)) + + -- Append to the existing array of relations + redis.call("SADD", inId .. ":" .. ARGS[1], omitId) + + return true +end + +--[[ + KEYS[1] = The id of the key we want to get the relations from + + ARGS[1] = The field the relations are stored at + ARGS[2] = Whether to return meta or entire object +]] +local function getJSONRelations(KEYS, ARGS) + local value = redis.call("SMEMBERS", KEYS[1] .. ":" .. ARGS[1]) + + if value ~= nil then + local out = {} + + if (ARGS[2] == "1") then + for i, id in ipairs(value) do + out[i] = cjson.decode(redis.call("JSON.GET", id)) + end + else + for i, id in ipairs(value) do + out[i] = cjson.decode(redis.call("JSON.GET", cjson.decode(redis.call("JSON.GET", id)).out)); + end + end + + return cjson.encode(out) + end + + return value +end + +local function getHASHRelations(KEYS, ARGS) + local value = redis.call("SMEMBERS", KEYS[1] .. ":" .. ARGS[1]) + + if value ~= nil then + local out = {} + + if (ARGS[2] == "1") then + for i, id in ipairs(value) do + out[i] = hashToJson(redis.call("HGETALL", id, "out")) + end + else + for i, id in ipairs(value) do + out[i] = hashToJson(redis.call("HGETALL", redis.call("HGET", id, "out"))) + end + end + + return cjson.encode(out) + end + + return value +end + +--[[ + KEYS[1] = Stringified JSON of [key] = search query as an array + + ARGS[1] = Whether to return meta or entire object +]] +local function searchJSONRelations(KEYS, ARGS) + local origin = cjson.decode(KEYS[1]) + local objs = {} + + for key, value in pairs(origin) do + objs[key] = {} + local val = redis.call("FT.SEARCH", value[1], value[2]) + + if ARGS[1] == "1" then + for i = 3, #val, 2 do + table.insert(objs[key], cjson.decode(val[i][2])) + end + else + for i = 3, #val, 2 do + table.insert(objs[key], cjson.decode(redis.call("JSON.GET", cjson.decode(val[i][2]).out))) + end + end + end + + return cjson.encode(objs) +end + + +local function searchHASHRelations(KEYS, ARGS) + local origin = cjson.decode(KEYS[1]) + local objs = {} + + for key, value in pairs(origin) do + objs[key] = {} + local val = redis.call("FT.SEARCH", value[1], value[2]) + + if ARGS[1] == "1" then + for i = 3, #val, 2 do + table.insert(objs[key], hashToJson(val[i])) + end + else + for i = 3, #val, 2 do + for j = 1, #val[i], 1 do + if (val[i][j] == "out") then + j = j + 1 + table.insert(objs[key], hashToJson(redis.call("HGETALL", val[i][j]))) + break + end + end + end + end + end + + return cjson.encode(objs) +end + +redis.register_function("JSONCR", createJSONRelation) +redis.register_function("HCR", createHASHRelation) +redis.register_function("JSONGR", getJSONRelations) +redis.register_function("HGR", getHASHRelations) +redis.register_function("JSONSR", searchJSONRelations) +redis.register_function("HSR", searchHASHRelations) diff --git a/src/search/search-builders/vector.ts b/src/search/search-builders/vector.ts index c5d2132..32e64a7 100644 --- a/src/search/search-builders/vector.ts +++ b/src/search/search-builders/vector.ts @@ -1,6 +1,6 @@ import { SearchField } from "./base"; -import type { ParseSchema } from "../../typings"; +import type { FloatArray, ParseSchema } from "../../typings"; import type { Search } from "../search"; export type VectorFunction = (vector: Vector) => Vector; @@ -11,7 +11,7 @@ export class Vector { public _type!: "RANGE" | "KNN"; /** @internal */ - public _buffer!: string; + public _buffer!: Buffer; /** @internal */ public _range?: number; @@ -30,8 +30,8 @@ export class Vector { return this; } - public from(data: Array | Float32Array | Float64Array): Vector { - this._buffer = Buffer.from(data).toString(); + public from(data: Array | FloatArray): Vector { + this._buffer = Buffer.from(Array.isArray(data) ? new Float32Array(data).buffer : data.buffer); return this; } diff --git a/src/search/search.ts b/src/search/search.ts index e018611..566c791 100644 --- a/src/search/search.ts +++ b/src/search/search.ts @@ -1,9 +1,9 @@ import { PrettyError } from "@infinite-fansub/logger"; -import { JSONDocument, HASHDocument } from "../document"; - import type { SearchOptions, SearchReply } from "redis"; +import type { JSONDocument, HASHDocument } from "../document"; + import { type SearchField, BooleanField, @@ -17,9 +17,10 @@ import { } from "./search-builders"; import type { - ParseSearchSchema, SearchInformation, + ParseSearchSchema, NodeRedisClient, + FieldStringType, MapSearchField, ReturnDocument, ParseSchema, @@ -36,8 +37,7 @@ export class Search, P extends ParseSearchSchema, P extends ParseSearchSchema(field: F): MapSearchField { @@ -111,7 +110,7 @@ export class Search, P extends ParseSearchSchema(offset: number, count: number, autoFetch?: F): Promise> | undefined> { const { total, documents } = await this.#search({ LIMIT: { from: offset, size: count } }); - if (total === 0) return void 0; + if (total === 0) return undefined; const docs = []; @@ -131,10 +130,11 @@ export class Search, P extends ParseSearchSchemathis.#schema, { + docs.push(new this.#doc(this.#schema, { globalPrefix: this.#information.globalPrefix, prefix: this.#information.prefix, - name: this.#information.modelName + name: this.#information.modelName, + suffix: this.#information.suffix }, doc.value, true, this.#information.skipDocumentValidation, autoFetch)); } @@ -143,7 +143,7 @@ export class Search, P extends ParseSearchSchema | undefined> { const { total, documents } = await this.#search({ LIMIT: { from: offset, size: count } }, true); - if (total === 0) return void 0; + if (total === 0) return undefined; const docs: Array = []; @@ -181,7 +181,7 @@ export class Search, P extends ParseSearchSchema(autoFetch?: F): Promise> | undefined> { const { total } = await this.#search({ LIMIT: { from: 0, size: 0 } }); - if (total === 0) return void 0; + if (total === 0) return undefined; const { documents } = await this.#search({ LIMIT: { from: 0, size: total } }); const docs = []; @@ -202,10 +202,11 @@ export class Search, P extends ParseSearchSchemaawait Promise.all(temp); } } - docs.push(new this.#docType(this.#schema, { + docs.push(new this.#doc(this.#schema, { globalPrefix: this.#information.globalPrefix, prefix: this.#information.prefix, - name: this.#information.modelName + name: this.#information.modelName, + suffix: this.#information.suffix }, doc.value, true, this.#information.skipDocumentValidation, autoFetch)); } @@ -214,7 +215,7 @@ export class Search, P extends ParseSearchSchema | undefined> { const { total } = await this.#search({ LIMIT: { from: 0, size: 0 } }); - if (total === 0) return void 0; + if (total === 0) return undefined; const { documents } = await this.#search({ LIMIT: { from: 0, size: total } }); const docs: Array = []; @@ -281,12 +282,13 @@ export class Search, P extends ParseSearchSchema | null> { const data = this.#information.dataStructure === "JSON" ? await this.#client.json.get(id) : await this.#client.hGetAll(id); - if (data === null) return null; + if (data === null || Object.keys(data).length === 0) return null; - return new this.#docType(this.#schema, { + return new this.#doc(this.#schema, { globalPrefix: this.#information.globalPrefix, prefix: this.#information.prefix, - name: this.#information.modelName + name: this.#information.modelName, + suffix: this.#information.suffix }, data, true, this.#information.skipDocumentValidation, false); } @@ -312,7 +314,7 @@ export class Search, P extends ParseSearchSchema, P extends ParseSearchSchemathis.#defineReturn(searchPath, type); } - #defineReturn(field: string, type: Exclude): BaseField { + #defineReturn(field: string, type: Exclude): BaseField { switch (type) { case "string": { this.#workingType = "string"; @@ -364,7 +366,6 @@ export class Search, P extends ParseSearchSchemanew VectorField(this, field); } - case "object": { throw new PrettyError("This should not be possible"); } } } diff --git a/src/typings/class-options.ts b/src/typings/class-options.ts new file mode 100644 index 0000000..2b545d7 --- /dev/null +++ b/src/typings/class-options.ts @@ -0,0 +1,76 @@ +import type { createClient } from "redis"; + +import type { SchemaDefinition } from "./schema-and-fields-definition"; +import type { MethodsDefinition } from "./methods-definition"; + +export type NodeRedisClient = ReturnType; + +export interface ClientOptions> { + url?: string | URLObject; + globalPrefix?: string; + enableInjections?: boolean; + base?: { + schema?: { + definition?: T, + methods?: M, + options?: SchemaOptions + } + }; +} + +export interface SchemaOptions { + dataStructure?: "HASH" | "JSON" | undefined; + + /** For word Stemming */ + language?: Language; + stopWords?: Array; + skipDocumentValidation?: boolean | undefined; + noLogs?: boolean | undefined; + prefix?: string | undefined; + suffix?: string | (() => string) | undefined; +} + +export interface ModelOptions extends Required> { + injectScripts: boolean; + globalPrefix: string; + prefix: string; +} + +/** internal */ +export interface ModelInformation extends ModelOptions, Required> { + modelName: string; +} + +/** internal */ +export interface SearchInformation extends ModelInformation { + searchIndex: string; +} + +export interface URLObject { + username: string; + password: string; + entrypoint: string; + port?: string; +} + +export type Language = "arabic" + | "armenian" + | "danish" + | "dutch" + | "english" + | "finnish" + | "french" + | "german" + | "hungarian" + | "italian" + | "norwegian" + | "portuguese" + | "romanian" + | "russian" + | "serbian" + | "spanish" + | "swedish" + | "tamil" + | "turkish" + | "yiddish" + | "chinese"; \ No newline at end of file diff --git a/src/typings/client-modules.ts b/src/typings/client-modules.ts new file mode 100644 index 0000000..09579d4 --- /dev/null +++ b/src/typings/client-modules.ts @@ -0,0 +1,12 @@ +import type { Client } from "../client"; + +export interface Module { + name: string; + ctor: new (client: Client) => unknown; +} + +export type ExtractName = Extract | ([T] extends [[]] ? [] : { [K in keyof T]: ExtractName }); + +export type WithModules, F = true> = F extends true ? { [N in M[number]["name"]]: CTORType } : never; + +type CTORType = T extends new (...args: Array) => infer U ? U : never; \ No newline at end of file diff --git a/src/typings/document-base.ts b/src/typings/document-base.ts new file mode 100644 index 0000000..94d7508 --- /dev/null +++ b/src/typings/document-base.ts @@ -0,0 +1,3 @@ +export interface DocumentShared { + toString: () => string | Array; +} \ No newline at end of file diff --git a/src/typings/extract-schema-generics.ts b/src/typings/extract-schema-generics.ts new file mode 100644 index 0000000..e1e35ff --- /dev/null +++ b/src/typings/extract-schema-generics.ts @@ -0,0 +1,5 @@ +import type { Schema } from "../schema"; + +export type ExtractSchemaDefinition = T extends Schema ? S : never; +export type ExtractSchemaMethods = T extends Schema ? M : never; +export type ExtractParsedSchemaDefinition = T extends Schema ? P : never; \ No newline at end of file diff --git a/src/typings/field-map.ts b/src/typings/field-map.ts index fec4666..bcf14fd 100644 --- a/src/typings/field-map.ts +++ b/src/typings/field-map.ts @@ -1,4 +1,4 @@ -import type { SchemaDefinition } from "./schema-definition"; +import type { FloatArray, InnerSchemaDefinition } from "./schema-and-fields-definition"; import type { Point } from "./point"; /** @@ -12,9 +12,10 @@ export interface FieldMap { text: string; date: Date | number; point: Point; - vector: Array | Float32Array | Float64Array; + vector: Array | FloatArray; array: Array; tuple: [T]; - object: Record; - reference: Array>; + object: Record; + reference: Array>; + relation: Array>; } \ No newline at end of file diff --git a/src/typings/index.ts b/src/typings/index.ts index fb7368b..9dc882e 100644 --- a/src/typings/index.ts +++ b/src/typings/index.ts @@ -1,19 +1,14 @@ -export type * from "./parsed-search-schema"; -export type * from "./search-information"; +export type * from "./schema-and-fields-definition"; +export type * from "./map-schema-to-search-strings"; +export type * from "./extract-schema-generics"; +export type * from "./parsed-schema-to-internal"; +export type * from "./map-schema-to-object"; export type * from "./methods-definition"; -export type * from "./schema-definition"; -export type * from "./map-search-fields"; -export type * from "./extract-generic"; -export type * from "./schema-options"; -export type * from "./model-options"; +export type * from "./return-document"; +export type * from "./client-modules"; +export type * from "./document-base"; +export type * from "./class-options"; export type * from "./parse-schema"; -export type * from "./point-units"; -export type * from "./return-doc"; -export type * from "./map-schema"; -export type * from "./url-object"; export type * from "./field-map"; -export type * from "./doc-base"; -export type * from "./modules"; -export type * from "./client"; export type * from "./utils"; export type * from "./point"; \ No newline at end of file diff --git a/src/typings/map-schema-to-object.ts b/src/typings/map-schema-to-object.ts new file mode 100644 index 0000000..6a74a1e --- /dev/null +++ b/src/typings/map-schema-to-object.ts @@ -0,0 +1,87 @@ +import type { ParseSchema, ParseSchemaData } from "./parse-schema"; +import type { ReferenceArray } from "../utils"; +import type { FieldMap } from "./field-map"; +import type { Expand } from "./utils"; + +export type MapSchema< + T extends ParseSchema, + FREF extends boolean = false, + FREL extends boolean = false, + MOR extends boolean = false, + CAS extends boolean = false +> = Expand & MapSchemaReferences & (CAS extends true ? unknown : FREL extends true ? MapSchemaRelations : unknown)>; + +export type MapSchemaData, CAS extends boolean = false> = { + [K in keyof T as T[K]["optional"] extends false + ? T[K]["default"] extends {} + ? CAS extends true + ? never + : K + : K + : T[K]["default"] extends {} + ? CAS extends true + ? never + : K + : never]: _MapSchemaData +} & { + [K in keyof T as T[K]["optional"] extends false + ? T[K]["default"] extends {} + ? CAS extends true + ? K + : never + : never + : T[K]["default"] extends {} + ? CAS extends true + ? K + : never + : K]?: _MapSchemaData + }; + +// eslint-disable-next-line @typescript-eslint/naming-convention +type _MapSchemaData[number]> = T extends { properties: unknown } + ? T["properties"] extends ParseSchemaData + ? Expand> + : T["properties"] extends ParseSchemaData + ? Expand> + : unknown + : T extends { elements: unknown } + ? T["elements"] extends [unknown, ...Array] + ? ParseTupleElements + : T["elements"] extends object + ? Array>> + : FieldMap["array"] + : T extends { algorithm: unknown } + ? T extends { vecType: "FLOAT32" } + ? Float32Array | Array + : T extends { vecType: "FLOAT64" } + ? Float64Array | Array + : unknown + : T extends { literal: unknown } + ? T["literal"] extends {} + ? T["literal"] extends Array + ? T["literal"][number] + : T["literal"] + : FieldMap[T["type"]] + : FieldMap[T["type"]] + ; + +type ParseTupleElements = { + [K in keyof T]: T[K] extends ParseSchemaData[number] ? _MapSchemaData : never +}; + +type MapSchemaReferences["references"], AF extends boolean = false, CAS extends boolean = false> = CAS extends true ? { + [K in keyof T]?: Array +} : { + [K in keyof T]: _MapSchemaReferences + }; + +// eslint-disable-next-line @typescript-eslint/naming-convention +type _MapSchemaReferences["references"][number], AF extends boolean = false> = AF extends true + ? Array>> + : ReferenceArray; + +type MapSchemaRelations["relations"], MOR extends boolean> = { + [K in keyof T]: MOR extends true + ? Array ? (T[K]["meta"] & {}) : any>>> + : Array ? (T[K]["schema"] & {}) : any>>> +}; \ No newline at end of file diff --git a/src/typings/map-schema-to-search-strings.ts b/src/typings/map-schema-to-search-strings.ts new file mode 100644 index 0000000..57a95b4 --- /dev/null +++ b/src/typings/map-schema-to-search-strings.ts @@ -0,0 +1,69 @@ +import type { ParseSchema } from "./parse-schema"; +import type { + BooleanField, + StringField, + NumberField, + BigIntField, + VectorField, + PointField, + TextField, + DateField +} from "../search/search-builders"; + +export type MapSearchField, T extends ParseSearchSchema> = T[K][0] extends "string" + ? StringField + : T[K][0] extends "number" + ? NumberField + : T[K][0] extends "bigint" + ? BigIntField + : T[K][0] extends "boolean" + ? BooleanField + : T[K][0] extends "text" + ? TextField + : T[K][0] extends "date" + ? DateField + : T[K][0] extends "point" + ? PointField + : T[K][0] extends "vector" + ? VectorField + : never; + +export type ParseSearchSchema["data"]> = { + [K in SchemaToStrings]: GetFinalProperty +}; + +type SchemaToStrings["data"], K extends keyof T = keyof T> = K extends string + ? T[K] extends { schema: unknown } + ? never + : T[K] extends { index: false } + ? never + : T[K] extends { properties: any } + ? `${K}.${SchemaToStrings}` + : T[K] extends { elements: unknown } + ? T[K]["elements"] extends [unknown, ...Array] + ? `${K}.${SchemaToStrings<{ + [U in keyof T[K]["elements"]as U extends `${number}` ? U : never]: T[K]["elements"][U] + }>}` + : T[K]["elements"] extends object + ? `${K}.${SchemaToStrings}` + : K + : K + : never; + +type GetFinalProperty["data"]> = T extends `${infer Head}.${infer Tail}` + ? S[Head] extends { properties: any } + ? GetFinalProperty + : S[Head] extends { elements: unknown } + ? GetFinalProperty + : never + : [S[T] extends { elements: unknown } + ? S[T]["elements"] extends {} + ? S[T]["elements"] + : "string" + : S[T]["type"], + S[T] extends { literal: unknown } + ? S[T]["literal"] extends Array + ? S[T]["literal"][number] + : S[T]["literal"] + : never + ]; \ No newline at end of file diff --git a/src/typings/methods-definition.ts b/src/typings/methods-definition.ts index f0d9c1f..7421e3c 100644 --- a/src/typings/methods-definition.ts +++ b/src/typings/methods-definition.ts @@ -1,4 +1,4 @@ -import type { SchemaDefinition } from "./schema-definition"; +import type { SchemaDefinition } from "./schema-and-fields-definition"; import type { Schema } from "../schema"; import type { Model } from "../model"; diff --git a/src/typings/parse-schema.ts b/src/typings/parse-schema.ts index f0456af..6973764 100644 --- a/src/typings/parse-schema.ts +++ b/src/typings/parse-schema.ts @@ -1,99 +1,128 @@ -import type { ExtractParsedSchemaDefinition } from "./extract-generic"; +import type { ExtractParsedSchemaDefinition } from "./extract-schema-generics"; import type { Schema } from "../schema"; import type { + InnerSchemaDefinition, SchemaDefinition, ReferenceField, + RelationField, ObjectField, StringField, NumberField, VectorField, + BigIntField, ArrayField, TupleField, FlatVector, HNSWVector, BaseField, - FieldType, - BigIntField -} from "./schema-definition"; + FieldType +} from "./schema-and-fields-definition"; export type ParseSchema = { - data: { - [K in keyof T as T[K] extends ReferenceField ? never : K]: T[K] extends ObjectField - ? { - [P in keyof Required]: P extends "properties" - ? T[K][P] extends {} - ? T[K][P] extends Schema - ? U - : T[K][P] extends SchemaDefinition - ? ParseSchema["data"] - : never - : undefined - : T[K][P] extends {} ? T[K][P] : Fill

- } - : T[K] extends ArrayField - ? { - [P in keyof Required]: P extends "elements" - ? T[K][P] extends SchemaDefinition ? ParseSchema["data"] - : T[K][P] extends {} ? T[K][P] : "string" - : Fill

- } - : T[K] extends TupleField - ? { - [P in keyof Required]: P extends "elements" - ? T extends Record> - ? { - [U in keyof V]: V[U] extends string - ? CreateDefinitionFromString - : V[U] extends FieldType - ? GetTupleObject - : never - } - : never - : T[K][P] extends {} ? T[K][P] : Fill

- } - : T[K] extends VectorField - ? T[K] extends FlatVector - ? { - [P in keyof Required]: T[K][P] extends {} ? T[K][P] : Fill

- } - : T[K] extends HNSWVector + data: ParseSchemaData, + references: { + [K in keyof T as T[K] extends ReferenceField ? K : never]: T[K] extends ReferenceField ? { - [P in keyof Required]: T[K][P] extends {} ? T[K][P] : Fill

+ [P in keyof ReferenceField]: P extends "schema" + ? T[K][P] extends "self" + ? ParseSchemaData + : ExtractParsedSchemaDefinition["data"] + : T[K][P] } : never - : T[K] extends StringField - ? { - [P in keyof Required]: T[K][P] extends {} ? T[K][P] : Fill

- } - : T[K] extends NumberField - ? { - [P in keyof Required]: T[K][P] extends {} ? T[K][P] : Fill

- } - : T[K] extends BigIntField - ? { - [P in keyof Required]: T[K][P] extends {} ? T[K][P] : Fill

- } - : T[K] extends BaseField - ? { - [P in keyof Required]: T[K][P] extends {} ? T[K][P] : Fill

- } - : CreateDefinitionFromString }, - references: { - [K in keyof T as T[K] extends ReferenceField ? K : never]: T[K] extends ReferenceField + relations: { + [K in keyof T as T[K] extends RelationField ? K : never]: T[K] extends RelationField ? { - [P in keyof ReferenceField]: P extends "schema" + [P in keyof Required]: P extends "schema" ? T[K][P] extends "self" - ? ParseSchema - : ExtractParsedSchemaDefinition + ? ParseSchemaData + : ExtractParsedSchemaDefinition["data"] + : P extends "meta" + ? T[K][P] extends {} + ? T[K][P] extends Schema + ? U["data"] & ParseSchemaData<{ in: "string", out: "string" }> + : T[K][P] extends InnerSchemaDefinition + ? ParseSchemaData + : never + : undefined : T[K][P] } : never } }; -export type CreateDefinitionFromString = T extends "vector" +export type ParseSchemaData = { + [K in keyof T as T[K] extends ReferenceField ? never : T[K] extends RelationField ? never : K]: T[K] extends ObjectField + ? ParseObjectField + : T[K] extends ArrayField + ? ParseArrayField + : T[K] extends TupleField + ? ParseTupleField + : T[K] extends VectorField + ? T[K] extends FlatVector + ? { + [P in keyof Required]: T[K][P] extends {} ? T[K][P] : Fill + } + : T[K] extends HNSWVector + ? { + [P in keyof Required]: T[K][P] extends {} ? T[K][P] : Fill + } + : never + : T[K] extends StringField + ? { + [P in keyof Required]: T[K][P] extends {} ? T[K][P] : Fill + } + : T[K] extends NumberField + ? { + [P in keyof Required]: T[K][P] extends {} ? T[K][P] : Fill + } + : T[K] extends BigIntField + ? { + [P in keyof Required]: T[K][P] extends {} ? T[K][P] : Fill + } + : T[K] extends BaseField + ? { + [P in keyof Required]: T[K][P] extends {} ? T[K][P] : Fill + } + : CreateDefinitionFromString +}; + +type ParseObjectField = { + [P in keyof Required]: P extends "properties" + ? T[P] extends {} + ? T[P] extends Schema + ? U["data"] + : T[P] extends InnerSchemaDefinition + ? ParseSchemaData + : never + : undefined + : T[P] extends {} ? T[P] : Fill +}; + +type ParseArrayField = { + [P in keyof Required]: P extends "elements" + ? T[P] extends InnerSchemaDefinition ? ParseSchemaData + : T[P] extends {} ? T[P] : "string" + : T[P] extends {} ? T[P] : Fill +}; + +type ParseTupleField = { + [P in keyof Required]: P extends "elements" + ? T extends Record + ? { + [U in keyof V]: V[U] extends string + ? CreateDefinitionFromString + : V[U] extends FieldType + ? GetTupleObject + : never + } + : never + : T[P] extends {} ? T[P] : Fill +}; + +type CreateDefinitionFromString = T extends "vector" ? { [K in keyof Required]: K extends "type" ? T @@ -105,41 +134,41 @@ export type CreateDefinitionFromString = T extends "vector" ? 128 : K extends "distance" ? "L2" - : Fill + : Fill } : T extends "string" ? { [K in keyof Required]: K extends "type" ? T - : Fill + : Fill } : T extends "number" ? { [K in keyof Required]: K extends "type" ? T - : Fill + : Fill } : T extends "bigint" ? { [K in keyof Required]: K extends "type" ? T - : Fill + : Fill } : { [K in keyof Required]: K extends "type" ? T - : Fill + : Fill }; /** * `T` is the same as `keyof FieldTypes` but i can't use `extends` here */ -export type Fill = T extends "optional" +type Fill = T extends "optional" ? false : T extends "sortable" ? false : T extends "index" - ? false + ? REL extends true ? true : false : undefined; -type GetTupleObject["data"]> = P extends { $: unknown } ? P["$"] : never; \ No newline at end of file +type GetTupleObject> = P extends { $: unknown } ? P["$"] : never; \ No newline at end of file diff --git a/src/typings/parsed-schema-to-internal.ts b/src/typings/parsed-schema-to-internal.ts new file mode 100644 index 0000000..07d31a8 --- /dev/null +++ b/src/typings/parsed-schema-to-internal.ts @@ -0,0 +1,13 @@ +import type { FieldStringType } from "./schema-and-fields-definition"; + +export interface ParsedSchemaToSearch { + map: ParsedMap; + index: Array; +} + +export type ParsedMap = Map, + searchPath: string +}>; + +export type ParsedRelationsToSearch = Record; \ No newline at end of file diff --git a/src/typings/point.ts b/src/typings/point.ts index b151f51..cd9ddf9 100644 --- a/src/typings/point.ts +++ b/src/typings/point.ts @@ -1 +1,3 @@ -export type Point = { longitude: number, latitude: number }; \ No newline at end of file +export type Point = { longitude: number, latitude: number }; + +export type Units = "m" | "km" | "ft" | "mi"; \ No newline at end of file diff --git a/src/typings/return-document.ts b/src/typings/return-document.ts new file mode 100644 index 0000000..4ed36e2 --- /dev/null +++ b/src/typings/return-document.ts @@ -0,0 +1,16 @@ +import type { HASHDocument, JSONDocument } from "../document"; +import type { MapSchema } from "./map-schema-to-object"; +import type { ParseSchema } from "./parse-schema"; +import type { Schema } from "../schema"; + +export type Document = JSONDocument | HASHDocument; + +export type ReturnDocument< + T extends Schema | ParseSchema, + FREF extends boolean = false, + FREL extends boolean = false, + MOR extends boolean = false, + CAS extends boolean = false +> = T extends Schema + ? Document & MapSchema + : T extends ParseSchema ? Document & MapSchema : never; \ No newline at end of file diff --git a/src/typings/schema-and-fields-definition.ts b/src/typings/schema-and-fields-definition.ts new file mode 100644 index 0000000..ea85b78 --- /dev/null +++ b/src/typings/schema-and-fields-definition.ts @@ -0,0 +1,197 @@ +import type { FieldMap } from "./field-map"; +import type { Schema } from "../schema"; +import type { Point } from "./point"; + +export type SchemaDefinition = Record; +export type InnerSchemaDefinition = Record; +export type SchemaField = FieldStringType | Exclude; + +export interface ParsedSchemaDefinition { + data: ParsedSchemaData; + references: ParsedSchemaReferences; + relations: ParsedSchemaRelations; +} + +export type ParsedSchemaData = Record; +export type ParsedSchemaReferences = Record; +export type ParsedSchemaRelations = Record; + +export type FieldType = StringField + | NumberField + | BigIntField + | BooleanField + | TextField + | DateField + | PointField + | ArrayField + | TupleField + | ObjectField + | ReferenceField + | VectorField + | RelationField; + +export type FieldStringType = keyof Omit; + +export type ParsedFieldType = ParsedStringField + | ParsedNumberField + | ParsedBigIntField + | ParsedObjectField + | ParsedArrayField + | ParsedTupleField + | Required + | Required + | Required + | Required + | Required; + +export type FloatArray = Float32Array | Float64Array; + +export interface BaseField { + type: keyof FieldMap; + default?: FieldMap[keyof FieldMap] | undefined; + optional?: boolean; + sortable?: boolean; + index?: boolean; +} + +// TAG +export interface StringField extends BaseField { + type: "string"; + default?: string | undefined; + literal?: string | Array | undefined; + caseSensitive?: boolean | undefined; +} + +export interface ParsedStringField extends Required { + literal: Array | undefined; +} + +// NUMERIC +export interface NumberField extends BaseField { + type: "number"; + default?: number | undefined; + literal?: number | Array | undefined; +} + +export interface ParsedNumberField extends Required { + literal: Array | undefined; +} + +// TAG +export interface BigIntField extends BaseField { + type: "bigint"; + default?: bigint | undefined; + literal?: bigint | Array | undefined; +} + +export interface ParsedBigIntField extends Required { + literal: Array | undefined; +} + +// TAG +export interface BooleanField extends BaseField { + type: "boolean"; + default?: boolean | undefined; +} + +// TEXT +export interface TextField extends BaseField { + type: "text"; + default?: string | undefined; + phonetic?: "dm:en" | "dm:fr" | "dm:pt" | "dm:es" | undefined; + weight?: number | undefined; +} + +// NUMERIC +export interface DateField extends Omit { + type: "date"; + default?: Date | number | string | undefined; +} + +// GEO +export interface PointField extends BaseField { + type: "point"; + default?: Point | undefined; +} + +// VECTOR +export interface BaseVector extends BaseField { + type: "vector"; + default?: Array | FloatArray | undefined; + algorithm: "FLAT" | "HNSW"; + vecType: "FLOAT32" | "FLOAT64"; + dim: number; + distance: "L2" | "IP" | "COSINE"; + cap?: number | undefined; +} + +export interface FlatVector extends BaseVector { + algorithm: "FLAT"; + size?: number | undefined; +} + +export interface HNSWVector extends BaseVector { + algorithm: "HNSW"; + m?: number | undefined; + construction?: number | undefined; + runtime?: number | undefined; + epsilon?: number | undefined; +} + +export type VectorField = FlatVector | HNSWVector; + +// FALLBACK +export interface ArrayField extends BaseField { + type: "array"; + elements?: Exclude | InnerSchemaDefinition | undefined; + default?: Array | undefined; + separator?: string; +} + +export interface ParsedArrayField extends Required { + type: "array"; + elements: Exclude | ParsedSchemaData; + default: Array | undefined; + + /** Default: `|` */ + separator: string; +} + +//FALLBACK +export interface TupleField extends Omit { + type: "tuple"; + elements: [SchemaField, ...Array]; + default?: Array | undefined; +} + +export interface ParsedTupleField extends Omit, "sortable"> { + type: "tuple"; + elements: [ParsedFieldType, ...Array]; + default: Array | undefined; +} + +// FALLBACK +export interface ObjectField extends Omit { + type: "object"; + properties?: Schema | InnerSchemaDefinition | undefined; + default?: Record | undefined; +} + +export interface ParsedObjectField extends Required> { + type: "object"; + properties: ParsedSchemaData | null; + default: Record | undefined; +} + +// NON EXISTENT HANDLE AS ARRAY OF STRINGS WITH AUTOFETCH TRANSFORMING INTO AN OBJECT +export interface ReferenceField extends Pick { + type: "reference"; + schema: Schema | "self"; +} + +// NON EXISTENT +export interface RelationField extends Pick { + type: "relation"; + schema: Schema | InnerSchemaDefinition | "self"; + meta?: Schema | InnerSchemaDefinition | undefined; +} \ No newline at end of file diff --git a/src/utils/index.ts b/src/utils/index.ts index 380340c..6161571 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,2 +1,2 @@ -export * from "./reference-array"; -export * from "./parse-to-search-index"; \ No newline at end of file +export * from "./parse-to-search-index"; +export * from "./reference-array"; \ No newline at end of file diff --git a/src/utils/parse-to-search-index.ts b/src/utils/parse-to-search-index.ts index 062db0c..e508f5b 100644 --- a/src/utils/parse-to-search-index.ts +++ b/src/utils/parse-to-search-index.ts @@ -1,4 +1,7 @@ +import { createHash } from "node:crypto"; + import type { + ParsedRelationsToSearch, ParsedSchemaDefinition, ParsedSchemaToSearch, ParsedArrayField, @@ -12,8 +15,8 @@ export function parseSchemaToSearchIndex( structure: "JSON" | "HASH", { previousKey, previousPath, arrayKey }: { previousKey?: string, previousPath?: string, arrayKey?: string } = {} ): ParsedSchemaToSearch { + const index: Array = []; let objs: ParsedMap = new Map(); - let index: Array = []; for (let i = 0, entries = Object.entries(schema), len = entries.length; i < len; i++) { const [key, value] = entries[i]; @@ -32,6 +35,7 @@ export function parseSchemaToSearchIndex( ); objs = new Map([...objs, ...parsed.map]); + index.push(...parsed.index); continue; } @@ -49,10 +53,9 @@ export function parseSchemaToSearchIndex( } ); objs = new Map([...objs, ...parsed.map]); + index.push(...parsed.index); continue; } - - arrayKey = `${arrayKey ? `${arrayKey}.${key}` : withPreviousKey}${getArrayModifier(value.elements)}`; } if (value.type === "tuple") { @@ -69,7 +72,7 @@ export function parseSchemaToSearchIndex( ); objs = new Map([...objs, ...parsed.map]); - + index.push(...parsed.index); } continue; } @@ -87,7 +90,7 @@ export function parseSchemaToSearchIndex( const prefix = structure === "JSON" ? "$." : ""; index.push( - `${prefix}${arrayKey ? arrayKey : withPreviousKey}`, + `${prefix}${arrayKey ? `${arrayKey}.${key}` : withPreviousKey}`, "AS", withPreviousPath, getSearchType(actualType) @@ -117,7 +120,7 @@ export function parseSchemaToSearchIndex( } } - if (value.sortable) index.push("SORTABLE"); + if ("sortable" in value && value.sortable) index.push("SORTABLE"); if (value.type === "string" && value.caseSensitive) index.push("CASESENSITIVE"); if (value.type === "text") { if (typeof value.phonetic !== "undefined") index.push("PHONETIC", value.phonetic); @@ -128,9 +131,32 @@ export function parseSchemaToSearchIndex( return { map: objs, index }; } -function getArrayModifier(elements: ParsedArrayField["elements"]): "*" | "[*]" | "" { - if (typeof elements === "object" || elements === "vector") return "[*]"; - if (elements === "string" || elements === "boolean") return "*"; +export function parseRelationsToSearchIndex( + schema: ParsedSchemaDefinition["relations"], + structure: "JSON" | "HASH", + initialKey: string +): ParsedRelationsToSearch { + const relations: ParsedRelationsToSearch = {}; + + for (let i = 0, entries = Object.entries(schema), len = entries.length; i < len; i++) { + const [key, value] = entries[i]; + + relations[key] = { + key: `${initialKey}-relation-${key}`, + hash: createHash("sha1").update(JSON.stringify({ + structure: structure, + definition: value.meta + })).digest("base64"), + data: parseSchemaToSearchIndex(value.meta, structure) + }; + } + + return relations; +} + +function getArrayModifier(elements: ParsedArrayField["elements"]): ".*" | ".[*]" | "" { + if (typeof elements === "object" || elements === "vector") return ".[*]"; + if (elements === "string" || elements === "boolean") return ".*"; return ""; } diff --git a/src/utils/reference-array.ts b/src/utils/reference-array.ts index 75956ef..44c1978 100644 --- a/src/utils/reference-array.ts +++ b/src/utils/reference-array.ts @@ -6,7 +6,7 @@ export class ReferenceArray extends Array { public reference(...recordOrDoc: Array): this { for (let i = 0, len = recordOrDoc.length; i < len; i++) { const tempVal = recordOrDoc[i]; - const tempId = tempVal instanceof JSONDocument || tempVal instanceof HASHDocument ? tempVal.$record_id : tempVal; + const tempId = tempVal instanceof JSONDocument || tempVal instanceof HASHDocument ? tempVal.$recordId : tempVal; if (this.includes(tempId)) continue; this.push(tempId); }