diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fabc682..4f7d79d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -19,9 +19,9 @@ jobs: uses: actions/checkout@v3 - name: Setup Deno - uses: denoland/setup-deno@v1 + uses: denoland/setup-deno@v2 with: - deno-version: v1.x + deno-version: v2.x - name: Check Types run: deno task check diff --git a/README.md b/README.md index 0eba38e..118fbb8 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,8 @@ dependencies by default. It's purpose is to enhance the experience of using Deno's KV store through additional features such as indexing, strongly typed collections and serialization/compression, while maintaining as much of the native functionality as possible, like atomic operations, real-time data updates -and queue listeners. Also works with other runtimes such as Node.js and Bun. +and queue listeners. Also works with other runtimes such as Node.js and Bun, and +has compatibility options for the browser. _Supported Deno verisons:_ **^1.43.0** @@ -35,7 +36,7 @@ _Supported Deno verisons:_ **^1.43.0** - [Collection Options](#collection-options) - [`idGenerator`](#idgenerator) - [`indices`](#indices) - - [`serialize`](#serialize) + - [`encoder`](#encoder) - [`history`](#history) - [Collection Methods](#collection-methods) - [find()](#find) @@ -98,12 +99,11 @@ _Supported Deno verisons:_ **^1.43.0** - [With checking](#with-checking) - [Document Methods](#document-methods) - [flat()](#flat) - - [Utility Functions](#utility-functions) - - [jsonSerialize()](#jsonserialize) - - [jsonDeserialize()](#jsondeserialize) - - [jsonStringify()](#jsonstringify) - - [jsonParse()](#jsonparse) - [Extensions](#extensions) + - [Encoding](#encoding) + - [JSON](#json) + - [V8](#v8) + - [Brotli](#brotli) - [Zod](#zod) - [Schemas](#schemas) - [Migrate](#migrate) @@ -131,7 +131,7 @@ type. Using the standard model strategy: ```ts -import { model } from "jsr:@olli/kvdex"; +import { model } from "@olli/kvdex"; type User = { username: string; @@ -178,29 +178,33 @@ const UserModel = z.object({ ## Database -`kvdex()` is used for creating a new database instance. It takes a Deno KV -instance and a schema definition as arguments. +`kvdex()` is used for creating a new database instance. It takes an options +object which expects a Deno KV instance and a schema definition. ```ts -import { kvdex, model, collection } from "jsr:@olli/kvdex" +import { kvdex, model, collection } from "@olli/kvdex" +import { jsonEncoder } from "@olli/kvdex/encoding/json" const kv = await Deno.openKv() -const db = kvdex(kv, { - numbers: collection(model()), - serializedStrings: collection(model(), { - serialize: "json" - }), - users: collection(UserModel, { - history: true, - indices: { - username: "primary" // unique - age: "secondary" // non-unique +const db = kvdex({ + kv: kv, + schema: { + numbers: collection(model()), + serializedStrings: collection(model(), { + encoder: jsonEncoder() + }), + users: collection(UserModel, { + history: true, + indices: { + username: "primary" // unique + age: "secondary" // non-unique + } + }), + // Nested collections + nested: { + strings: collection(model()), } - }), - // Nested collections - nested: { - strings: collection(model()), } }) ``` @@ -208,8 +212,8 @@ const db = kvdex(kv, { The schema definition contains collection builders, or nested schema definitions. Collections can hold any type adhering to KvValue. -**Note:** Index values are always serialized, using JSON-serialization by -default. +**Note:** Index values are always serialized, using the JSON-encoder by default, +or alternatively your provided encoder. ## Collection Options @@ -227,28 +231,34 @@ added, which can be useful to create derived ids. The default id generator uses Id created from the data being added: ```ts -import { collection, kvdex, model } from "jsr:@olli/kvdex"; +import { collection, kvdex, model } from "@olli/kvdex"; const kv = await Deno.openKv(); -const db = kvdex(kv, { - users: collection(model(), { - idGenerator: (user) => user.username, - }), +const db = kvdex({ + kv: kv, + schema: { + users: collection(model(), { + idGenerator: (user) => user.username, + }), + }, }); ``` Using randomely generated uuids: ```ts -import { collection, kvdex, model } from "jsr:@olli/kvdex"; +import { collection, kvdex, model } from "@olli/kvdex"; const kv = await Deno.openKv(); -const db = kvdex(kv, { - users: collection(model(), { - idGenerator: () => crypto.randomUUID(), - }), +const db = kvdex({ + kv: kv, + schema: { + users: collection(model(), { + idGenerator: () => crypto.randomUUID(), + }), + }, }); ``` @@ -260,59 +270,70 @@ querying data based on index values. **NOTE:** Index values are always serialized. ```ts -import { collection, kvdex, model } from "jsr:@olli/kvdex"; +import { collection, kvdex, model } from "@olli/kvdex"; const kv = await Deno.openKv(); -const db = kvdex(kv, { - users: collection(model(), { - indices: { - username: "primary", // unique - age: "secondary", // non-unique - }, - }), +const db = kvdex({ + kv: kv, + schema: { + users: collection(model(), { + indices: { + username: "primary", // unique + age: "secondary", // non-unique + }, + }), + }, }); ``` -### `serialize` +### `encoder` -Specify serialization for the collection. This lets large objects that exceed -the native size limit of 64kb to be stored, by serializing, compressing and -dividing the value across multiple key/value entries. When serialization is -used, there is a tradeoff between speed and storage efficiency. If the serialize -option is not set, or if a custom serialize configuration is used, then JSON -serialization is used by default for any unset serialize functions, while Brotli -compression is used for any unset compress functions. V8 serialization and -Brotli compression rely on runtime implementations, and are therefore only -compatible with runtimes that implement them (Deno, Node.js). +Specify serialization and compression for the collection. This lets large +objects that exceed the native size limit of 64kb to be stored, by serializing, +compressing and dividing the value across multiple key/value entries. When an +encoder is specified, there is a tradeoff between speed and storage efficiency. +For storing objects larger than the atomic operation size limit, see +[Blob Storage](#blob-storage). ```ts -import { kvdex, collection, model } from "jsr:@olli/kvdex" +import { kvdex, collection, model } from "@olli/kvdex" +import { jsonEncoder } from "@olli/kvdex/encoding/json" +import { v8Encoder } from "@olli/kvdex/encoding/v8" +import { brotliCompression } from "@olli/kvdex/encoding/brotli" const kv = await Deno.openKv() -const db = kvdex(kv, { - users: collection(model(), { - // Custom JSON serializer + Brotli compression - serialize: "json", - - // Custom JSON serializer and no compression (best runtime compatibility) - serialize: "json-uncompressed", - - // Built-in V8 serializer + Brotli compression, - serialize: "v8", - - // Built-in V8 serializer and no compression - serialize: "v8-uncompressed", - - // Set custom serialize, deserialize, compress and decompress functions - serialize: { - serialize: ..., - deserialize: ..., - compress: ..., - decompress: ..., - } - }), +const db = kvdex({ + kv: kv, + schema: { + users: collection(model(), { + // JSON-encoder without compression (best runtime compatibility) + encoder: jsonEncoder(), + + // JSON-encoder + Brotli compression (requires node:zlib built-in) + encoder: jsonEncoder({ compression: brotliCompression() }), + + // V8-encoder without brotli compression (requires node:v8 built-in) + encoder: v8Encoder() + + // V8-encoder + brotli compression (requires node:v8 and node:zlib built-in) + encoder: v8Encoder({ compression: brotliCompression() }) + + // Set custom serialize, deserialize, compress and decompress functions + encoder: { + serializer: { + serialize: ..., + deserialize: ..., + }, + // optional + compressor: { + compress: ..., + decompress: ..., + } + } + }), + } }) ``` @@ -321,14 +342,17 @@ const db = kvdex(kv, { Set to `true` to enable version history. Default is `false`. ```ts -import { collection, kvdex, model } from "jsr:@olli/kvdex"; +import { collection, kvdex, model } from "@olli/kvdex"; const kv = await Deno.openKv(); -const db = kvdex(kv, { - users: collection(model(), { - history: true, - }), +const db = kvdex({ + kv: kv, + schema: { + users: collection(model(), { + history: true, + }), + }, }); ``` @@ -1273,19 +1297,20 @@ method expects a selector for selecting the collection that the subsequent mutation actions will be performed on. Mutations can be performed on documents from multiple collections in a single atomic operation by calling "select" at any point in the building chain to switch the collection context. To execute the -operation, call "commit" at the end of the chain. An atomic operation returns a -Deno.KvCommitResult object if successful, and Deno.KvCommitError if not. +operation, call "commit" at the end of the chain. A committed atomic operation +returns a promise resolving to either a Deno.KvCommitResult object if +successful, or Deno.KvCommitError if not. **_NOTE_:** Atomic operations are not available for serialized collections. For indexable collections, any operations performing deletes will not be truly -atomic in the sense that it performs a single isolated operation. The reason for -this being that the document data must be read before performing the initial -delete operation, to then perform another delete operation for the index -entries. If the initial operation fails, the index entries will not be deleted. -To avoid collisions and errors related to indexing, an atomic operation will -always fail if it is trying to delete and write to the same indexable -collection. It will also fail if trying to set/add a document with colliding -index entries. +atomic in the sense that it performs a single isolated operation. This is +because the document data must be read before performing the initial delete +operation, to then perform another delete operation for the index entries. If +the initial operation fails, the index entries will not be deleted. To avoid +collisions and errors related to indexing, an atomic operation will always fail +if it is trying to delete and write to the same indexable collection. It will +also fail if trying to set/add a document with colliding index entries, or if +trying to set a document with the `overwrite` option. ### Without checking @@ -1385,76 +1410,104 @@ const flattened = doc.flat(); // } ``` -## Utility Functions +## Extensions + +Additional features outside of the basic functionality provided by `kvdex`. +While the core functionalities are free of third-party dependencies, extended +features may rely on third-party dependenices or runtime-specific APIs to +enhance integration. + +### Encoding -These are additional utility functions that are exposed and can be used outside -of `kvdex`. +Utilities for encoding data. -### jsonSerialize() +#### JSON -Serialize a JSON-like value to a Uint8Array. +JSON-encoder and utilities for stringifying and serializing data. ```ts -import { jsonSerialize } from "@olli/kvdex"; +import { jsonEncoder } from "@olli/kvdex/encoding/json"; -const serialized = jsonSerialize({ - foo: "foo", - bar: "bar", - bigint: 10n, -}); +// With default options (no compression) +const encoder = jsonEncoder(); ``` -### jsonDeserialize() +```ts +import { jsonEncoder } from "@olli/kvdex/encoding/json"; +import { brotliCompressor } from "@olli/kvdex/encoding/brotli"; -Deserialize a value that was serialized using `jsonSerialize()`. +// With brotli compression +const encoder = jsonEncoder({ compressor: brotliCompressor() }); +``` ```ts -import { jsonDeserialize, jsonSerialize } from "@olli/kvdex"; +import { jsonParse, jsonStringify } from "@olli/kvdex/encoding/json"; +// Stringify value +const json = jsonStringify({ + foo: "bar", + big: 100n, +}); + +// Parse value +const value = jsonParse(json); +``` + +```ts +import { jsonDeserialize, jsonSerialize } from "@olli/kvdex/encoding/json"; + +// Serialize value as Uint8Array const serialized = jsonSerialize({ - foo: "foo", - bar: "bar", - bigint: 10n, + foo: "bar", + big: 100n, }); +// Deserialize value from Uint8Array const value = jsonDeserialize(serialized); ``` -### jsonStringify() +#### V8 -Stringify a JSON-like value. +V8-encoder and serialization utilities. Relies on the `node:v8` built-in. ```ts -import { jsonStringify } from "@olli/kvdex"; - -const str = jsonStringify({ - foo: "foo", - bar: "bar", - bigint: 10n, -}); -``` +import { v8Encoder } from "@olli/kvdex/encoding/v8"; +import { brotliCompressor } from "@olli/kvdex/encoding/brotli"; -### jsonParse() +// V8-encoder without compression +const encoder = v8Encoder(); -Parse a value that was stringified using `jsonStringify()` +// V8-encoder with brotli compression +const encoder = v8Encoder({ compressor: brotliCompressor() }); +``` ```ts -import { jsonParse, jsonStringify } from "@olli/kvdex"; +import { v8Deserialize, v8Serialize } from "@olli/kvdex/encoding/v8"; -const str = jsonStringify({ - foo: "foo", - bar: "bar", - bigint: 10n, +// Serialize value as Uint8Array +const serialized = v8Serialize({ + foo: "bar", + big: 100n, }); -const value = jsonParse(str); +// Deserialize value from Uint8Array +const value = v8Deserialize(serialized); ``` -## Extensions +#### Brotli -Additional features outside of the basic functionality provided by `kvdex`. -While the core functionalities are dependency-free, extended features may rely -on some dependenices to enhance integration. +Easy to configure brotli compression for use with the `encoder` option for +collections. Relies on the `node:zlib` built-in. + +```ts +import { brotliCompressor } from "@olli/kvdex/encoding/brotli"; + +// With default options +const compressor = brotliCompressor(); + +// Explicitly set quality level (default is 1) +const compressor = brotliCompressor({ quality: 2 }); +``` ### Zod @@ -1468,7 +1521,7 @@ schemas. ```ts import { z } from "npm:zod"; -import { KvIdSchema } from "jsr:@olli/kvdex/zod"; +import { KvIdSchema } from "@olli/kvdex/zod"; const UserSchema = z.object({ username: z.string(), @@ -1502,7 +1555,7 @@ Use the migrate function and pass a source KV instance and a target KV instance. Optionally pass `all: true` to migrate all entries. ```ts -import { migrate } from "jsr:@olli/kvdex/migrate"; +import { migrate } from "@olli/kvdex/migrate"; const source = await Deno.openKv("./source.sqlite3"); const target = await Deno.openKv("./target.sqlite3"); @@ -1525,18 +1578,18 @@ import { MapKv } from "@olli/kvdex/kv"; // Create a database from a `MapKv` instance, using `Map` as it's backend by default. const kv = new MapKv(); // Equivalent to `new MapKv({ map: new Map() })` -const db = kvdex(kv, {}); +const db = kvdex({ kv }); ``` ```ts import { kvdex } from "@olli/kvdex"; import { MapKv, StorageAdapter } from "@olli/kvdex/kv"; -// Create an ephimeral database from a `MapKv` instance, +// Create a temporary database from a `MapKv` instance, // explicitly using `localStorage` as it's backend. const map = new StorageAdapter(localStorage); const kv = new MapKv({ map, clearOnClose: true }); -const db = kvdex(kv, {}); +const db = kvdex({ kv }); ``` ## Blob Storage @@ -1547,11 +1600,15 @@ can be used. By default, batching is disabled to ensure consistency and improve performance. ```ts -import { collection, kvdex, model } from "jsr:@olli/kvdex" +import { collection, kvdex, model } from "@olli/kvdex" +import { jsonEncoder } from "@olli/kvdex/encoding/json" const kv = await Deno.openKv() -const db = kvdex(kv, { - blobs: collection(model(), { serialize: "json" }), +const db = kvdex({ + kv: kv, + schema: { + blobs: collection(model(), { encoder: jsonEncoder() }), + } }) const blob = // read from disk, etc. diff --git a/benchmarks/db/kvdex.bench.ts b/benchmarks/db/kvdex.bench.ts index dc346c6..f0557cc 100644 --- a/benchmarks/db/kvdex.bench.ts +++ b/benchmarks/db/kvdex.bench.ts @@ -5,17 +5,20 @@ Deno.bench("db - kvdex (10 collections)", async (b) => { await useKv((kv) => { b.start(); - kvdex(kv, { - 1: collection(model()), - 2: collection(model()), - 3: collection(model()), - 4: collection(model()), - 5: collection(model()), - 6: collection(model()), - 7: collection(model()), - 8: collection(model()), - 9: collection(model()), - 10: collection(model()), + kvdex({ + kv, + schema: { + 1: collection(model()), + 2: collection(model()), + 3: collection(model()), + 4: collection(model()), + 5: collection(model()), + 6: collection(model()), + 7: collection(model()), + 8: collection(model()), + 9: collection(model()), + 10: collection(model()), + }, }); b.end(); @@ -26,107 +29,110 @@ Deno.bench("db - kvdex (100 collections)", async (b) => { await useKv((kv) => { b.start(); - kvdex(kv, { - 1: collection(model()), - 2: collection(model()), - 3: collection(model()), - 4: collection(model()), - 5: collection(model()), - 6: collection(model()), - 7: collection(model()), - 8: collection(model()), - 9: collection(model()), - 10: collection(model()), - 11: collection(model()), - 12: collection(model()), - 13: collection(model()), - 14: collection(model()), - 15: collection(model()), - 16: collection(model()), - 17: collection(model()), - 18: collection(model()), - 19: collection(model()), - 20: collection(model()), - 21: collection(model()), - 22: collection(model()), - 23: collection(model()), - 24: collection(model()), - 25: collection(model()), - 26: collection(model()), - 27: collection(model()), - 28: collection(model()), - 29: collection(model()), - 30: collection(model()), - 31: collection(model()), - 32: collection(model()), - 33: collection(model()), - 34: collection(model()), - 35: collection(model()), - 36: collection(model()), - 37: collection(model()), - 38: collection(model()), - 39: collection(model()), - 40: collection(model()), - 41: collection(model()), - 42: collection(model()), - 43: collection(model()), - 44: collection(model()), - 45: collection(model()), - 46: collection(model()), - 47: collection(model()), - 48: collection(model()), - 49: collection(model()), - 50: collection(model()), - 51: collection(model()), - 52: collection(model()), - 53: collection(model()), - 54: collection(model()), - 55: collection(model()), - 56: collection(model()), - 57: collection(model()), - 58: collection(model()), - 59: collection(model()), - 60: collection(model()), - 61: collection(model()), - 62: collection(model()), - 63: collection(model()), - 64: collection(model()), - 65: collection(model()), - 66: collection(model()), - 67: collection(model()), - 68: collection(model()), - 69: collection(model()), - 70: collection(model()), - 71: collection(model()), - 72: collection(model()), - 73: collection(model()), - 74: collection(model()), - 75: collection(model()), - 76: collection(model()), - 77: collection(model()), - 78: collection(model()), - 79: collection(model()), - 80: collection(model()), - 81: collection(model()), - 82: collection(model()), - 83: collection(model()), - 84: collection(model()), - 85: collection(model()), - 86: collection(model()), - 87: collection(model()), - 88: collection(model()), - 89: collection(model()), - 90: collection(model()), - 91: collection(model()), - 92: collection(model()), - 93: collection(model()), - 94: collection(model()), - 95: collection(model()), - 96: collection(model()), - 97: collection(model()), - 98: collection(model()), - 99: collection(model()), - 100: collection(model()), + kvdex({ + kv, + schema: { + 1: collection(model()), + 2: collection(model()), + 3: collection(model()), + 4: collection(model()), + 5: collection(model()), + 6: collection(model()), + 7: collection(model()), + 8: collection(model()), + 9: collection(model()), + 10: collection(model()), + 11: collection(model()), + 12: collection(model()), + 13: collection(model()), + 14: collection(model()), + 15: collection(model()), + 16: collection(model()), + 17: collection(model()), + 18: collection(model()), + 19: collection(model()), + 20: collection(model()), + 21: collection(model()), + 22: collection(model()), + 23: collection(model()), + 24: collection(model()), + 25: collection(model()), + 26: collection(model()), + 27: collection(model()), + 28: collection(model()), + 29: collection(model()), + 30: collection(model()), + 31: collection(model()), + 32: collection(model()), + 33: collection(model()), + 34: collection(model()), + 35: collection(model()), + 36: collection(model()), + 37: collection(model()), + 38: collection(model()), + 39: collection(model()), + 40: collection(model()), + 41: collection(model()), + 42: collection(model()), + 43: collection(model()), + 44: collection(model()), + 45: collection(model()), + 46: collection(model()), + 47: collection(model()), + 48: collection(model()), + 49: collection(model()), + 50: collection(model()), + 51: collection(model()), + 52: collection(model()), + 53: collection(model()), + 54: collection(model()), + 55: collection(model()), + 56: collection(model()), + 57: collection(model()), + 58: collection(model()), + 59: collection(model()), + 60: collection(model()), + 61: collection(model()), + 62: collection(model()), + 63: collection(model()), + 64: collection(model()), + 65: collection(model()), + 66: collection(model()), + 67: collection(model()), + 68: collection(model()), + 69: collection(model()), + 70: collection(model()), + 71: collection(model()), + 72: collection(model()), + 73: collection(model()), + 74: collection(model()), + 75: collection(model()), + 76: collection(model()), + 77: collection(model()), + 78: collection(model()), + 79: collection(model()), + 80: collection(model()), + 81: collection(model()), + 82: collection(model()), + 83: collection(model()), + 84: collection(model()), + 85: collection(model()), + 86: collection(model()), + 87: collection(model()), + 88: collection(model()), + 89: collection(model()), + 90: collection(model()), + 91: collection(model()), + 92: collection(model()), + 93: collection(model()), + 94: collection(model()), + 95: collection(model()), + 96: collection(model()), + 97: collection(model()), + 98: collection(model()), + 99: collection(model()), + 100: collection(model()), + }, }); b.end(); diff --git a/benchmarks/utils/deserialize.bench.ts b/benchmarks/utils/deserialize.bench.ts index 6795fe1..c4f011c 100644 --- a/benchmarks/utils/deserialize.bench.ts +++ b/benchmarks/utils/deserialize.bench.ts @@ -1,10 +1,10 @@ -import { obj } from "./_object.ts"; import { jsonDeserialize, jsonSerialize, v8Deserialize, v8Serialize, -} from "../../src/utils.ts"; +} from "../../src/ext/encoding/mod.ts"; +import { obj } from "./_object.ts"; const js = jsonSerialize(obj); const ds = v8Serialize(obj); diff --git a/benchmarks/utils/serialize.bench.ts b/benchmarks/utils/serialize.bench.ts index dba55ed..0ecf1a2 100644 --- a/benchmarks/utils/serialize.bench.ts +++ b/benchmarks/utils/serialize.bench.ts @@ -1,5 +1,5 @@ +import { jsonSerialize, v8Serialize } from "../../src/ext/encoding/mod.ts"; import { obj } from "./_object.ts"; -import { jsonSerialize, v8Serialize } from "../../src/utils.ts"; Deno.bench("utils - jsonSerialize", () => { jsonSerialize(obj); diff --git a/deno.json b/deno.json index 8c60fdf..0924a30 100644 --- a/deno.json +++ b/deno.json @@ -1,11 +1,15 @@ { "name": "@olli/kvdex", - "version": "2.1.2", + "version": "3.0.0", "exports": { ".": "./mod.ts", "./zod": "./src/ext/zod/mod.ts", "./migrate": "./src/ext/migrate/mod.ts", - "./kv": "./src/ext/kv/mod.ts" + "./kv": "./src/ext/kv/mod.ts", + "./encoding": "./src/ext/encoding/mod.ts", + "./encoding/json": "./src/ext/encoding/json/mod.ts", + "./encoding/v8": "./src/ext/encoding/v8/mod.ts", + "./encoding/brotli": "./src/ext/encoding/brotli/mod.ts" }, "tasks": { "check": "deno check **/*.ts", @@ -25,5 +29,14 @@ }, "bench": { "include": ["./benchmarks"] + }, + "imports": { + "@deno/kv": "npm:@deno/kv@^0.8.4", + "@std/assert": "jsr:@std/assert@1.0.7", + "@std/bytes": "jsr:@std/bytes@^1.0.4", + "@std/cli": "jsr:@std/cli@^1.0.6", + "@std/collections": "jsr:@std/collections@^1.0.9", + "@std/ulid": "jsr:@std/ulid@^1.0.0", + "zod": "npm:zod@^3.23.8" } } diff --git a/deno.lock b/deno.lock index f85a8b2..0f777f8 100644 --- a/deno.lock +++ b/deno.lock @@ -1,100 +1,147 @@ { - "version": "3", - "packages": { - "specifiers": { - "jsr:@std/assert@^0.217": "jsr:@std/assert@0.217.0", - "jsr:@std/assert@^0.217.0": "jsr:@std/assert@0.217.0", - "jsr:@std/assert@^0.220.1": "jsr:@std/assert@0.220.1", - "jsr:@std/bytes@^1.0.1": "jsr:@std/bytes@1.0.2", - "jsr:@std/cli@^0.217": "jsr:@std/cli@0.217.0", - "jsr:@std/cli@^0.220": "jsr:@std/cli@0.220.1", - "jsr:@std/collections@^1.0.2": "jsr:@std/collections@1.0.5", - "jsr:@std/fmt@^0.217.0": "jsr:@std/fmt@0.217.0", - "jsr:@std/ulid@^0.224.1": "jsr:@std/ulid@0.224.1", - "jsr:@std/ulid@^1.0.0": "jsr:@std/ulid@1.0.0", - "npm:@deno/kv": "npm:@deno/kv@0.7.0", - "npm:@types/node": "npm:@types/node@18.16.19", - "npm:zod@^3.22": "npm:zod@3.23.8" - }, - "jsr": { - "@std/assert@0.217.0": { - "integrity": "c98e279362ca6982d5285c3b89517b757c1e3477ee9f14eb2fdf80a45aaa9642", - "dependencies": [ - "jsr:@std/fmt@^0.217.0" - ] - }, - "@std/assert@0.220.1": { - "integrity": "88710d54f3afdd7a5761e7805abba1f56cd14e4b212feffeb3e73a9f77482425" - }, - "@std/bytes@1.0.1": { - "integrity": "e57c9b243932b95a4c3672f8a038cdadea7492efeeb6b8a774844fee70426815" - }, - "@std/bytes@1.0.2": { - "integrity": "fbdee322bbd8c599a6af186a1603b3355e59a5fb1baa139f8f4c3c9a1b3e3d57" - }, - "@std/cli@0.217.0": { - "integrity": "5c25bd040cd0bbc11707ecaa7ce9c41314d02ce46f45709822bbd0f92c35fee8", - "dependencies": [ - "jsr:@std/assert@^0.217.0" - ] - }, - "@std/cli@0.220.1": { - "integrity": "6c38388efc899c9d98811d38a46effe01e0d91138068483f1e6ae834caad5e82", - "dependencies": [ - "jsr:@std/assert@^0.220.1" - ] - }, - "@std/collections@1.0.2": { - "integrity": "cb1834be534f967e86495f6c64fa06f6b3debdaab5b860c2d3194db59cd30157" - }, - "@std/collections@1.0.5": { - "integrity": "ab9eac23b57a0c0b89ba45134e61561f69f3d001f37235a248ed40be260c0c10" - }, - "@std/fmt@0.217.0": { - "integrity": "cb99f82500b8da20202fedfa8bb94dd0222e81f0494ed9087de20ee3d8a39a8d" - }, - "@std/ulid@0.224.1": { - "integrity": "4de06fdb030ff3990b1b0344e330373c11ce6051ac449fc435667a485a6723fa" - }, - "@std/ulid@1.0.0": { - "integrity": "d41c3d27a907714413649fee864b7cde8d42ee68437d22b79d5de4f81d808780" - } - }, - "npm": { - "@deno/kv-darwin-arm64@0.7.0": { - "integrity": "sha512-PaTriht31Rb08TwWdAkmODrLNnGnl9l8nlF/0aae/v7dzJ5FeaoX+0RgxTD+m90quG3ynsdLsY8iCa+st+MkiA==", - "dependencies": {} - }, - "@deno/kv-darwin-x64@0.7.0": { - "integrity": "sha512-Yw4Gmn6opUJ6ZVuztNaGS4yt11KyJI/juTAyzNiY5H3VhHD7xVm+peG4SGkr6Hq/vXVGcQM3Lldw5b3aJJQYSg==", - "dependencies": {} - }, - "@deno/kv-linux-x64-gnu@0.7.0": { - "integrity": "sha512-D6xJcx4HSUbgomxGV2lGUgKrRmwHNMmCbu8XRxBjIgpsKrFK1W6w+KitI92wMCgEIOAFAnfeyrYxtMzh2ZdYTw==", - "dependencies": {} - }, - "@deno/kv-win32-x64-msvc@0.7.0": { - "integrity": "sha512-0UWf6a+T7e/1uOcUgUKTrMupIQSnCI02856ecu9JwutajSvvbSgoi+jpmaUnYW6QqcXjY8ECI/28CQi1DvYYhw==", - "dependencies": {} - }, - "@deno/kv@0.7.0": { - "integrity": "sha512-+vYCIWDQq+03RKwh85WDBVplwT7gimcLyLezj6C3OuMwaTWfPy9NVmA5y4Mwpoht0+H4CjknEUE7LrfZJAcQ7w==", - "dependencies": { - "@deno/kv-darwin-arm64": "@deno/kv-darwin-arm64@0.7.0", - "@deno/kv-darwin-x64": "@deno/kv-darwin-x64@0.7.0", - "@deno/kv-linux-x64-gnu": "@deno/kv-linux-x64-gnu@0.7.0", - "@deno/kv-win32-x64-msvc": "@deno/kv-win32-x64-msvc@0.7.0" - } - }, - "@types/node@18.16.19": { - "integrity": "sha512-IXl7o+R9iti9eBW4Wg2hx1xQDig183jj7YLn8F7udNceyfkbn1ZxmzZXuak20gR40D7pIkIY1kYGx5VIGbaHKA==", - "dependencies": {} - }, - "zod@3.23.8": { - "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", - "dependencies": {} - } + "version": "4", + "specifiers": { + "jsr:@std/assert@0.217": "0.217.0", + "jsr:@std/assert@1.0.7": "1.0.7", + "jsr:@std/assert@~0.220.1": "0.220.1", + "jsr:@std/bytes@^1.0.1": "1.0.2", + "jsr:@std/bytes@^1.0.4": "1.0.4", + "jsr:@std/cli@0.217": "0.217.0", + "jsr:@std/cli@0.220": "0.220.1", + "jsr:@std/cli@^1.0.6": "1.0.6", + "jsr:@std/collections@^1.0.2": "1.0.5", + "jsr:@std/collections@^1.0.9": "1.0.9", + "jsr:@std/fmt@0.217": "0.217.0", + "jsr:@std/internal@^1.0.5": "1.0.5", + "jsr:@std/ulid@1": "1.0.0", + "jsr:@std/ulid@~0.224.1": "0.224.1", + "npm:@deno/kv@*": "0.7.0", + "npm:@deno/kv@~0.8.4": "0.8.4", + "npm:@types/node@*": "18.16.19", + "npm:zod@^3.22.0": "3.23.8", + "npm:zod@^3.23.8": "3.23.8" + }, + "jsr": { + "@std/assert@0.217.0": { + "integrity": "c98e279362ca6982d5285c3b89517b757c1e3477ee9f14eb2fdf80a45aaa9642", + "dependencies": [ + "jsr:@std/fmt" + ] + }, + "@std/assert@0.220.1": { + "integrity": "88710d54f3afdd7a5761e7805abba1f56cd14e4b212feffeb3e73a9f77482425" + }, + "@std/assert@1.0.7": { + "integrity": "64ce9fac879e0b9f3042a89b3c3f8ccfc9c984391af19e2087513a79d73e28c3", + "dependencies": [ + "jsr:@std/internal" + ] + }, + "@std/bytes@1.0.1": { + "integrity": "e57c9b243932b95a4c3672f8a038cdadea7492efeeb6b8a774844fee70426815" + }, + "@std/bytes@1.0.2": { + "integrity": "fbdee322bbd8c599a6af186a1603b3355e59a5fb1baa139f8f4c3c9a1b3e3d57" + }, + "@std/bytes@1.0.4": { + "integrity": "11a0debe522707c95c7b7ef89b478c13fb1583a7cfb9a85674cd2cc2e3a28abc" + }, + "@std/cli@0.217.0": { + "integrity": "5c25bd040cd0bbc11707ecaa7ce9c41314d02ce46f45709822bbd0f92c35fee8", + "dependencies": [ + "jsr:@std/assert@0.217" + ] + }, + "@std/cli@0.220.1": { + "integrity": "6c38388efc899c9d98811d38a46effe01e0d91138068483f1e6ae834caad5e82", + "dependencies": [ + "jsr:@std/assert@~0.220.1" + ] + }, + "@std/cli@1.0.6": { + "integrity": "d22d8b38c66c666d7ad1f2a66c5b122da1704f985d3c47f01129f05abb6c5d3d" + }, + "@std/collections@1.0.2": { + "integrity": "cb1834be534f967e86495f6c64fa06f6b3debdaab5b860c2d3194db59cd30157" + }, + "@std/collections@1.0.5": { + "integrity": "ab9eac23b57a0c0b89ba45134e61561f69f3d001f37235a248ed40be260c0c10" + }, + "@std/collections@1.0.9": { + "integrity": "4f58104ead08a04a2199374247f07befe50ba01d9cca8cbb23ab9a0419921e71" + }, + "@std/fmt@0.217.0": { + "integrity": "cb99f82500b8da20202fedfa8bb94dd0222e81f0494ed9087de20ee3d8a39a8d" + }, + "@std/internal@1.0.5": { + "integrity": "54a546004f769c1ac9e025abd15a76b6671ddc9687e2313b67376125650dc7ba" + }, + "@std/ulid@0.224.1": { + "integrity": "4de06fdb030ff3990b1b0344e330373c11ce6051ac449fc435667a485a6723fa" + }, + "@std/ulid@1.0.0": { + "integrity": "d41c3d27a907714413649fee864b7cde8d42ee68437d22b79d5de4f81d808780" + } + }, + "npm": { + "@deno/kv-darwin-arm64@0.7.0": { + "integrity": "sha512-PaTriht31Rb08TwWdAkmODrLNnGnl9l8nlF/0aae/v7dzJ5FeaoX+0RgxTD+m90quG3ynsdLsY8iCa+st+MkiA==" + }, + "@deno/kv-darwin-arm64@0.8.4": { + "integrity": "sha512-j86nnE1QdLw20OrUs/6Iw6ZYzC8pmfU1+K4hNSVHO9K0bfy3VBd4JSHkHLmYCiHDkgIm+wTxct33thl6HxXz0Q==" + }, + "@deno/kv-darwin-x64@0.7.0": { + "integrity": "sha512-Yw4Gmn6opUJ6ZVuztNaGS4yt11KyJI/juTAyzNiY5H3VhHD7xVm+peG4SGkr6Hq/vXVGcQM3Lldw5b3aJJQYSg==" + }, + "@deno/kv-darwin-x64@0.8.4": { + "integrity": "sha512-qdczxcqkN2fbDX/nIzUetI6i8usNu8kpN3sDV0rXcSWlg9E5huWWjGp6PbOS4w1xarUWbqFAZvy4VSmGTVN1Zw==" + }, + "@deno/kv-linux-x64-gnu@0.7.0": { + "integrity": "sha512-D6xJcx4HSUbgomxGV2lGUgKrRmwHNMmCbu8XRxBjIgpsKrFK1W6w+KitI92wMCgEIOAFAnfeyrYxtMzh2ZdYTw==" + }, + "@deno/kv-linux-x64-gnu@0.8.4": { + "integrity": "sha512-xv2rM6wrVHVOM4Nswl8iyfdZZiEp5r85jwLajj0NGTiLMAQLBtDsBE/kpH4Ap3K6yiqJX3nTb44Z8AJ+IyzO4Q==" + }, + "@deno/kv-win32-x64-msvc@0.7.0": { + "integrity": "sha512-0UWf6a+T7e/1uOcUgUKTrMupIQSnCI02856ecu9JwutajSvvbSgoi+jpmaUnYW6QqcXjY8ECI/28CQi1DvYYhw==" + }, + "@deno/kv-win32-x64-msvc@0.8.4": { + "integrity": "sha512-xTEByTpC1DWw4A1F9isD8B16v1+CQFHFvi/Mm2bqlO9iD5HfIGgalWJbL3EvgYeybQ9yA27KGqaGnKxXdaX5Rg==" + }, + "@deno/kv@0.7.0": { + "integrity": "sha512-+vYCIWDQq+03RKwh85WDBVplwT7gimcLyLezj6C3OuMwaTWfPy9NVmA5y4Mwpoht0+H4CjknEUE7LrfZJAcQ7w==", + "dependencies": [ + "@deno/kv-darwin-arm64@0.7.0", + "@deno/kv-darwin-x64@0.7.0", + "@deno/kv-linux-x64-gnu@0.7.0", + "@deno/kv-win32-x64-msvc@0.7.0" + ] + }, + "@deno/kv@0.8.4": { + "integrity": "sha512-5q2izU1tp6wv8rDIwMb6GXe/B+aO/sjAjRAOIigEtX+qOiTLsPE++ibJbfafVb0LmjEdlA18Kpfo23fln73OtQ==", + "dependencies": [ + "@deno/kv-darwin-arm64@0.8.4", + "@deno/kv-darwin-x64@0.8.4", + "@deno/kv-linux-x64-gnu@0.8.4", + "@deno/kv-win32-x64-msvc@0.8.4" + ] + }, + "@types/node@18.16.19": { + "integrity": "sha512-IXl7o+R9iti9eBW4Wg2hx1xQDig183jj7YLn8F7udNceyfkbn1ZxmzZXuak20gR40D7pIkIY1kYGx5VIGbaHKA==" + }, + "zod@3.23.8": { + "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==" } }, - "remote": {} + "workspace": { + "dependencies": [ + "jsr:@std/assert@1.0.7", + "jsr:@std/bytes@^1.0.4", + "jsr:@std/cli@^1.0.6", + "jsr:@std/collections@^1.0.9", + "jsr:@std/ulid@1", + "npm:@deno/kv@~0.8.4", + "npm:zod@^3.23.8" + ] + } } diff --git a/mod.ts b/mod.ts index ccbcfaa..bd31968 100644 --- a/mod.ts +++ b/mod.ts @@ -5,14 +5,6 @@ export { Collection, collection } from "./src/collection.ts"; export { AtomicBuilder } from "./src/atomic_builder.ts"; export { Document } from "./src/document.ts"; -// Expose reusable utilities -export { - jsonDeserialize, - jsonParse, - jsonSerialize, - jsonStringify, -} from "./src/utils.ts"; - // Expose errors export * from "./src/errors.ts"; diff --git a/src/atomic_builder.ts b/src/atomic_builder.ts index 28e3585..4f08818 100644 --- a/src/atomic_builder.ts +++ b/src/atomic_builder.ts @@ -1,5 +1,5 @@ import type { Collection } from "./collection.ts"; -import { ulid } from "./deps.ts"; +import { ulid } from "@std/ulid"; import { InvalidCollectionError } from "./errors.ts"; import type { AtomicCheck, @@ -12,6 +12,7 @@ import type { DenoKvCommitError, DenoKvCommitResult, DenoKvU64, + EmptyObject, EnqueueOptions, HistoryEntry, KvObject, @@ -70,7 +71,7 @@ export class AtomicBuilder< operations?: Operations, ) { // Check for large collection - if (collection._isSerialized) { + if (collection._encoder) { throw new InvalidCollectionError( "Atomic operations are not supported for serialized collections", ); @@ -139,7 +140,7 @@ export class AtomicBuilder< */ add( value: TInput, - options?: AtomicSetOptions, + options?: AtomicSetOptions, ): this { return this.setDocument(null, value, options); } @@ -165,7 +166,7 @@ export class AtomicBuilder< set( id: ParseId, value: TInput, - options?: AtomicSetOptions, + options?: AtomicSetOptions, ): this { return this.setDocument(id, value, options); } @@ -511,8 +512,17 @@ export class AtomicBuilder< private setDocument( id: ParseId | null, value: TInput, - options?: AtomicSetOptions, + options?: AtomicSetOptions, ) { + const overwrite = !!(options as AtomicSetOptions | undefined) + ?.overwrite; + + if (this.collection._isIndexable && overwrite) { + throw new InvalidCollectionError( + "The overwrite property is not supported for indexable collections", + ); + } + this.operations.asyncMutations.push(async () => { // Create id key from collection id key and id const collection = this.collection; @@ -524,9 +534,10 @@ export class AtomicBuilder< const idKey = extendKey(collection._keys.id, docId); // Add set operation - this.operations.atomic - .check({ key: idKey, versionstamp: null }) - .set(idKey, parsed, options); + this.operations.atomic.set(idKey, parsed, options); + if (!overwrite) { + this.operations.atomic.check({ key: idKey, versionstamp: null }); + } if (collection._isIndexable) { // Add collection id key for collision detection diff --git a/src/atomic_pool.ts b/src/atomic_pool.ts index 88832df..079af6e 100644 --- a/src/atomic_pool.ts +++ b/src/atomic_pool.ts @@ -1,9 +1,9 @@ import type { - AtomicSetOptions, DenoAtomicCheck, DenoAtomicOperation, DenoKvCommitError, DenoKvCommitResult, + DenoKvSetOptions, DenoKvStrictKey, } from "./types.ts"; @@ -15,7 +15,7 @@ export class AtomicPool implements DenoAtomicOperation { this.pool = []; } - set(key: DenoKvStrictKey, value: unknown, options?: AtomicSetOptions) { + set(key: DenoKvStrictKey, value: unknown, options?: DenoKvSetOptions) { this.pool.push((op) => op.set(key, value, options)); return this; } diff --git a/src/atomic_wrapper.ts b/src/atomic_wrapper.ts index 7d1fc82..9e51dc0 100644 --- a/src/atomic_wrapper.ts +++ b/src/atomic_wrapper.ts @@ -5,12 +5,12 @@ import { ATOMIC_OPERTION_CHECK_LIMIT, } from "./constants.ts"; import type { - AtomicSetOptions, DenoAtomicCheck, DenoAtomicOperation, DenoKv, DenoKvCommitError, DenoKvCommitResult, + DenoKvSetOptions, DenoKvStrictKey, } from "./types.ts"; @@ -37,7 +37,7 @@ export class AtomicWrapper implements DenoAtomicOperation { this.currentKeySize = 0; } - set(key: DenoKvStrictKey, value: unknown, options?: AtomicSetOptions) { + set(key: DenoKvStrictKey, value: unknown, options?: DenoKvSetOptions) { this.addMutation((op) => op.set(key, value, options), 67, 2, false); return this; } diff --git a/src/collection.ts b/src/collection.ts index 00f68ca..9f6e34b 100644 --- a/src/collection.ts +++ b/src/collection.ts @@ -9,6 +9,8 @@ import type { DenoKvCommitResult, DenoKvEntryMaybe, DenoKvStrictKey, + EncodedEntry, + Encoder, EnqueueOptions, FindManyOptions, FindOptions, @@ -35,33 +37,30 @@ import type { QueueListenerOptions, QueueMessageHandler, SecondaryIndexKeys, - SerializedEntry, - Serializer, SetOptions, UpdateData, UpdateManyOptions, UpdateOneOptions, UpdateOptions, UpdateStrategy, + WatchManager, WatchOptions, } from "./types.ts"; import { allFulfilled, checkIndices, - compress, createHandlerId, createListOptions, createListSelector, createSecondaryIndexKeyPrefix, createWatcher, - decompress, + decodeData, deleteIndices, + encodeData, extendKey, generateId, getDocumentId, isKvObject, - jsonDeserialize, - jsonSerialize, kvGetMany, prepareEnqueue, selectsAll, @@ -82,32 +81,38 @@ import { AtomicWrapper } from "./atomic_wrapper.ts"; import { AtomicPool } from "./atomic_pool.ts"; import { Document } from "./document.ts"; import { model as m } from "./model.ts"; -import { concat, deepMerge, ulid } from "./deps.ts"; -import { v8Serialize } from "./utils.ts"; -import { v8Deserialize } from "./utils.ts"; +import { deepMerge } from "@std/collections/deep-merge"; +import { concat } from "@std/bytes/concat"; +import { ulid } from "@std/ulid"; /** * Create a new collection within a database context. * * @example * ```ts - * import { model, collection, kvdex } from "jsr:@olli/kvdex" + * import { model, collection, kvdex } from "@olli/kvdex" + * import { jsonEncoder } from "@olli/kvdex/encoding/json" * * type User = { * username: string * age: number * } * - * const db = kvdex(kv, { - * numbers: collection(model()), - * users: collection(model(), { - * idGenerator: () => crypto.randomUUID(), - * serialize: "json", - * indices: { - * username: "primary", - * age: "secondary" - * } - * }) + * const kv = await Deno.openKv() + * + * const db = kvdex({ + * kv: kv, + * schema: { + * numbers: collection(model()), + * users: collection(model(), { + * idGenerator: () => crypto.randomUUID(), + * encoder: jsonEncoder(), + * indices: { + * username: "primary", + * age: "secondary" + * } + * }) + * } * }) * ``` * @@ -154,9 +159,8 @@ export class Collection< readonly _secondaryIndexList: string[]; readonly _keys: CollectionKeys; readonly _idGenerator: IdGenerator>; - readonly _serializer: Serializer; + readonly _encoder?: Encoder; readonly _isIndexable: boolean; - readonly _isSerialized: boolean; readonly _keepsHistory: boolean; constructor( @@ -173,6 +177,7 @@ export class Collection< this.idempotentListener = idempotentListener; this._model = model; this._idGenerator = options?.idGenerator ?? generateId as any; + this._encoder = options?.encoder; // Set keys this._keys = { @@ -230,47 +235,6 @@ export class Collection< } }); - // Set serialization - this._isSerialized = !!opts?.serialize; - - if (opts?.serialize === "v8") { - this._serializer = { - serialize: v8Serialize, - deserialize: v8Deserialize, - compress, - decompress, - }; - } else if (opts?.serialize === "v8-uncompressed") { - this._serializer = { - serialize: v8Serialize, - deserialize: v8Deserialize, - compress: (v) => v, - decompress: (v) => v, - }; - } else if (opts?.serialize === "json") { - this._serializer = { - serialize: jsonSerialize, - deserialize: jsonDeserialize, - compress, - decompress, - }; - } else if (opts?.serialize === "json-uncompressed") { - this._serializer = { - serialize: jsonSerialize, - deserialize: jsonDeserialize, - compress: (v) => v, - decompress: (v) => v, - }; - } else { - this._serializer = { - serialize: jsonSerialize, - deserialize: jsonDeserialize, - compress, - decompress, - ...opts?.serialize, - }; - } - // Set isIndexable flag this._isIndexable = this._primaryIndexList.length > 0 || this._secondaryIndexList.length > 0; @@ -333,14 +297,13 @@ export class Collection< options?: FindOptions, ): Promise> | null> { // Serialize and compress index value - const serialized = await this._serializer.serialize(value); - const compressed = await this._serializer.compress(serialized); + const encoded = await encodeData(value, this._encoder); // Create the index key const key = extendKey( this._keys.primaryIndex, index as KvId, - compressed, + encoded, ); // Get index entry @@ -382,14 +345,13 @@ export class Collection< >, ): Promise>>> { // Serialize and compress index value - const serialized = await this._serializer.serialize(value); - const compressed = await this._serializer.compress(serialized); + const encoded = await encodeData(value, this._encoder); // Create prefix key const prefixKey = extendKey( this._keys.secondaryIndex, index as KvId, - compressed, + encoded, ); // Add documents to result list by secondary index @@ -494,8 +456,8 @@ export class Collection< let historyEntry = value as HistoryEntry; // Handle serialized entries - if (historyEntry.type === "write" && this._isSerialized) { - const { ids } = historyEntry.value as SerializedEntry; + if (historyEntry.type === "write" && this._encoder) { + const { ids } = historyEntry.value as EncodedEntry; const timeId = getDocumentId(key as DenoKvStrictKey)!; const keys = ids.map((segmentId) => @@ -508,15 +470,12 @@ export class Collection< const data = concat(entries.map((entry) => entry.value as Uint8Array)); // Decompress and deserialize - const serialized = await this._serializer.decompress(data); - const deserialized = await this._serializer.deserialize( - serialized, - ); + const decoded = await decodeData(data, this._encoder); // Set history entry historyEntry = { ...historyEntry, - value: this._model.parse?.(deserialized), + value: this._model.parse?.(decoded), }; } else if (historyEntry.type === "write") { // Set history entry @@ -627,14 +586,13 @@ export class Collection< options?: FindOptions, ): Promise { // Serialize and compress index value - const serialized = await this._serializer.serialize(value); - const compressed = await this._serializer.compress(serialized); + const encoded = await encodeData(value, this._encoder); // Create index key const key = extendKey( this._keys.primaryIndex, index as KvId, - compressed, + encoded, ); // Get index entry @@ -684,14 +642,13 @@ export class Collection< >, ): Promise { // Serialize and compress index value - const serialized = await this._serializer.serialize(value); - const compressed = await this._serializer.compress(serialized); + const encoded = await encodeData(value, this._encoder); // Create prefix key const prefixKey = extendKey( this._keys.secondaryIndex, index as KvId, - compressed, + encoded, ); // Delete documents by secondary index, return iterator cursor @@ -847,14 +804,13 @@ export class Collection< > > { // Serialize and compress index value - const serialized = await this._serializer.serialize(value); - const compressed = await this._serializer.compress(serialized); + const encoded = await encodeData(value, this._encoder); // Create prefix key const prefixKey = extendKey( this._keys.secondaryIndex, index as KvId, - compressed, + encoded, ); // Update each document by secondary index, add commit result to result list @@ -1792,14 +1748,13 @@ export class Collection< >, ): Promise>> { // Serialize and compress index value - const serialized = await this._serializer.serialize(value); - const compressed = await this._serializer.compress(serialized); + const encoded = await encodeData(value, this._encoder); // Create prefix key const prefixKey = extendKey( this._keys.secondaryIndex, index as KvId, - compressed, + encoded, ); // Execute callback function for each document entry, return result and cursor @@ -1919,14 +1874,13 @@ export class Collection< >, ): Promise { // Serialize and compress index value - const serialized = await this._serializer.serialize(value); - const compressed = await this._serializer.compress(serialized); + const encoded = await encodeData(value, this._encoder); // Create prefix key const prefixKey = extendKey( this._keys.secondaryIndex, index as KvId, - compressed, + encoded, ); // Initialize count result @@ -2186,10 +2140,7 @@ export class Collection< id: ParseId, fn: (doc: Document> | null) => unknown, options?: WatchOptions, - ): { - promise: Promise; - cancel: () => Promise; - } { + ): WatchManager { const key = extendKey(this._keys.id, id); return createWatcher(this.kv, options, [key], async (entries) => { @@ -2247,10 +2198,7 @@ export class Collection< ids: ParseId[], fn: (doc: (Document> | null)[]) => unknown, options?: WatchOptions, - ): { - promise: Promise; - cancel: () => Promise; - } { + ): WatchManager { const keys = ids.map((id) => extendKey(this._keys.id, id)); return createWatcher(this.kv, options, keys, async (entries) => { @@ -2326,16 +2274,15 @@ export class Collection< } // Serialize if enabled - if (this._isSerialized) { - const serialized = isUint8Array - ? value - : await this._serializer.serialize(value); - const compressed = await this._serializer.compress(serialized); + if (this._encoder) { + const encoded = isUint8Array + ? await this._encoder.compressor?.compress(value) ?? value + : await encodeData(value, this._encoder); // Set segmented entries let index = 0; - for (let i = 0; i < compressed.length; i += UINT8ARRAY_LENGTH_LIMIT) { - const part = compressed.subarray(i, i + UINT8ARRAY_LENGTH_LIMIT); + for (let i = 0; i < encoded.length; i += UINT8ARRAY_LENGTH_LIMIT) { + const part = encoded.subarray(i, i + UINT8ARRAY_LENGTH_LIMIT); const key = extendKey(this._keys.segment, docId, index); ids.push(index); @@ -2357,7 +2304,7 @@ export class Collection< } // Set serialized document value - const serializedEntry: SerializedEntry = { + const serializedEntry: EncodedEntry = { ids, isUint8Array, }; @@ -2430,8 +2377,8 @@ export class Collection< failedAtomic.delete(historyKey); } - if (this._isSerialized) { - const { ids } = docValue as SerializedEntry; + if (this._encoder) { + const { ids } = docValue as EncodedEntry; ids.forEach((id) => failedAtomic.delete(extendKey(this._keys.segment, docId, id)) ); @@ -2514,7 +2461,7 @@ export class Collection< } // If serialized, delete existing segment entries - if (this._isSerialized) { + if (this._encoder) { const atomic = new AtomicWrapper(this.kv); const keyPrefix = extendKey(this._keys.segment, id); const iter = this.kv.list({ prefix: keyPrefix }); @@ -2576,9 +2523,9 @@ export class Collection< return null; } - if (this._isSerialized) { + if (this._encoder) { // Get document parts - const { ids, isUint8Array } = value as SerializedEntry; + const { ids, isUint8Array } = value as EncodedEntry; const keys = ids.map((segId) => extendKey(this._keys.segment, docId, segId) @@ -2590,15 +2537,14 @@ export class Collection< const data = concat(docEntries.map((entry) => entry.value as Uint8Array)); // Decompress and deserialize - const serialized = await this._serializer.decompress(data); - const deserialized = isUint8Array - ? serialized as TOutput - : await this._serializer.deserialize(serialized); + const decoded = isUint8Array + ? (await this._encoder?.compressor?.decompress(data) ?? data) as TOutput + : await decodeData(data, this._encoder); // Return parsed document return new Document>(this._model, { id: docId as ParseId, - value: deserialized, + value: decoded, versionstamp, }); } @@ -2725,7 +2671,7 @@ export class Collection< }); } - if (this._isIndexable && this._isSerialized) { + if (this._isIndexable && this._encoder) { // Run delete operations for each id await allFulfilled(ids.map(async (id) => { // Create document id key, get entry and construct document @@ -2737,7 +2683,7 @@ export class Collection< atomic.delete(idKey); if (entry.value) { - const keys = (entry.value as SerializedEntry).ids.map((segId) => + const keys = (entry.value as EncodedEntry).ids.map((segId) => extendKey(this._keys.segment, id, segId) ); @@ -2776,7 +2722,7 @@ export class Collection< return; } - if (this._isSerialized) { + if (this._encoder) { // Perform delete for each id await allFulfilled(ids.map(async (id) => { // Create document id key, get document value @@ -2791,7 +2737,7 @@ export class Collection< // Delete document entries atomic.delete(idKey); - const keys = (value as SerializedEntry).ids.map((segId) => + const keys = (value as EncodedEntry).ids.map((segId) => extendKey(this._keys.segment, id, segId) ); diff --git a/src/constants.ts b/src/constants.ts index a39186b..f08ed5b 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -36,8 +36,6 @@ export const DEFAULT_LOOP_RETRY = 10; export const DEFAULT_UPDATE_STRATEGY: UpdateStrategy = "merge"; // Misc -export const COMPRESSION_QUALITY_LEVEL = 1; - export const MIN_INTERVAL_START_DELAY = 1_000; export const MIN_LOOP_START_DELAY = 1_000; diff --git a/src/deps.ts b/src/deps.ts deleted file mode 100644 index df330e6..0000000 --- a/src/deps.ts +++ /dev/null @@ -1,8 +0,0 @@ -export { brotliCompressSync, brotliDecompressSync, constants } from "node:zlib"; -export { ulid } from "jsr:@std/ulid@^1.0.0"; -export { concat } from "jsr:@std/bytes@^1.0.1/concat"; -export { - deepMerge, - type DeepMergeOptions, -} from "jsr:@std/collections@^1.0.2/deep-merge"; -export { deserialize, serialize } from "node:v8"; diff --git a/src/ext/encoding/brotli/brotli_compressor.ts b/src/ext/encoding/brotli/brotli_compressor.ts new file mode 100644 index 0000000..3850703 --- /dev/null +++ b/src/ext/encoding/brotli/brotli_compressor.ts @@ -0,0 +1,41 @@ +import type { Compressor } from "../../../types.ts"; +import { brotliCompressSync, brotliDecompressSync, constants } from "node:zlib"; +import type { BrotliCompressorOptions } from "./types.ts"; + +/** + * Brotli compressor. + * + * Used for compressing and compressing data represented as a Uint8Array. + * + * @param options - Brolti compression options. + * @returns - A Compressor object. + */ +export function brotliCompressor( + options?: BrotliCompressorOptions, +): Compressor { + return new BrotliCompressor(options?.quality); +} + +class BrotliCompressor implements Compressor { + private quality; + + constructor(quality: number = 1) { + this.quality = quality; + } + + compress(data: Uint8Array) { + const buffer = brotliCompressSync(data, { + params: { [constants.BROTLI_PARAM_QUALITY]: this.quality }, + }); + + return new Uint8Array(buffer); + } + + decompress(data: Uint8Array) { + const buffer = brotliDecompressSync(data, { + params: { [constants.BROTLI_PARAM_QUALITY]: this.quality }, + }); + + return new Uint8Array(buffer); + } +} diff --git a/src/ext/encoding/brotli/mod.ts b/src/ext/encoding/brotli/mod.ts new file mode 100644 index 0000000..2b60940 --- /dev/null +++ b/src/ext/encoding/brotli/mod.ts @@ -0,0 +1,21 @@ +/** + * @module # Brotli + * + * Easy to configure brotli compression for use with the `encoder` option for collections. + * + * Relies on the `node:zlib` built-in. + * + * @example + * ```ts + * import { brotliCompressor } from "@olli/kvdex/encoding/brotli" + * + * // With default options + * const compressor = brotliCompressor() + * + * // Explicitly set quality level (default is 1) + * const compressor = brotliCompressor({ quality: 2 }) + * ``` + */ + +export { brotliCompressor } from "./brotli_compressor.ts"; +export type * from "./types.ts"; diff --git a/src/ext/encoding/brotli/types.ts b/src/ext/encoding/brotli/types.ts new file mode 100644 index 0000000..c5a664e --- /dev/null +++ b/src/ext/encoding/brotli/types.ts @@ -0,0 +1,9 @@ +/** Options for brotli compression. */ +export type BrotliCompressorOptions = { + /** + * Brolti compression quality. + * + * @default 1 + */ + quality?: number; +}; diff --git a/src/ext/encoding/json/json_encoder.ts b/src/ext/encoding/json/json_encoder.ts new file mode 100644 index 0000000..887b396 --- /dev/null +++ b/src/ext/encoding/json/json_encoder.ts @@ -0,0 +1,21 @@ +import type { Encoder } from "../../../types.ts"; +import type { JsonEncoderOptions } from "./types.ts"; +import { jsonDeserialize, jsonSerialize } from "./utils.ts"; + +/** + * JSON-encoder. + * + * Used for serializing and deserializing data as Uint8Array. + * + * @param options - JSON encoding options. + * @returns - An Encoder object. + */ +export function jsonEncoder(options?: JsonEncoderOptions): Encoder { + return { + serializer: { + serialize: jsonSerialize, + deserialize: jsonDeserialize, + }, + compressor: options?.compressor, + }; +} diff --git a/src/ext/encoding/json/mod.ts b/src/ext/encoding/json/mod.ts new file mode 100644 index 0000000..7866ae2 --- /dev/null +++ b/src/ext/encoding/json/mod.ts @@ -0,0 +1,59 @@ +/** + * @module # JSON + * + * JSON-encoder and utilities for stringifying and serializing data. + * + * @example + * ```ts + * import { jsonEncoder } from "@olli/kvdex/encoding/json" + * + * // With default options (no compression) + * const encoder = jsonEncoder() + * ``` + * + * @example + * ```ts + * import { jsonEncoder } from "@olli/kvdex/encoding/json" + * import { brotliCompressor } from "@olli/kvdex/encoding/brotli" + * + * // With brotli compression + * const encoder = jsonEncoder({ compressor: brotliCompressor() }) + * ``` + * + * @example + * ```ts + * import { jsonStringify, jsonParse } from "@olli/kvdex/encoding/json" + * + * // Stringify value + * const json = jsonStringify({ + * foo: "bar", + * big: 100n + * }) + * + * // Parse value + * const value = jsonParse(json) + * ``` + * + * @example + * ```ts + * import { jsonSerialize, jsonDeserialize } from "@olli/kvdex/encoding/json" + * + * // Serialize value as Uint8Array + * const serialized = jsonSerialize({ + * foo: "bar", + * big: 100n + * }) + * + * // Deserialize value from Uint8Array + * const value = jsonDeserialize(serialized) + * ``` + */ + +export { jsonEncoder } from "./json_encoder.ts"; +export { + jsonDeserialize, + jsonParse, + jsonSerialize, + jsonStringify, +} from "./utils.ts"; +export type * from "./types.ts"; diff --git a/src/ext/encoding/json/types.ts b/src/ext/encoding/json/types.ts new file mode 100644 index 0000000..46ee497 --- /dev/null +++ b/src/ext/encoding/json/types.ts @@ -0,0 +1,7 @@ +import type { Compressor } from "../../../types.ts"; + +/** Options for JSON encoding. */ +export type JsonEncoderOptions = { + /** Optional compressor object. */ + compressor?: Compressor; +}; diff --git a/src/ext/encoding/json/utils.ts b/src/ext/encoding/json/utils.ts new file mode 100644 index 0000000..1810bb5 --- /dev/null +++ b/src/ext/encoding/json/utils.ts @@ -0,0 +1,569 @@ +import type { KvObject, KvValue } from "../../../types.ts"; +import { isKvObject } from "../../../utils.ts"; + +const TEXT_ENCODER = new TextEncoder(); +const TEXT_DECODER = new TextDecoder(); + +/** + * Serialize a JSON-like value to a Uint8Array. + * + * @example + * ```ts + * import { jsonSerialize } from "@olli/kvdex" + * + * const serialized = jsonSerialize({ + * foo: "foo", + * bar: "bar", + * bigint: 10n + * }) + * ``` + * + * @param value - Value to be serialized. + * @returns Serialized value. + */ +export function jsonSerialize(value: unknown): Uint8Array { + const str = jsonStringify(value); + return TEXT_ENCODER.encode(str); +} + +/** + * Deserialize a value that was serialized using `jsonSerialize()`. + * + * @example + * ```ts + * import { jsonSerialize, jsonDeserialize } from "@olli/kvdex" + * + * const serialized = jsonSerialize({ + * foo: "foo", + * bar: "bar", + * bigint: 10n + * }) + * + * const value = jsonDeserialize(serialized) + * ``` + * + * @param value - Value to be deserialize. + * @returns Deserialized value. + */ +export function jsonDeserialize(value: Uint8Array): T { + const str = TEXT_DECODER.decode(value); + return jsonParse(str); +} + +/** + * Stringify a JSON-like value. + * + * @example + * ```ts + * import { jsonStringify } from "@olli/kvdex" + * + * const str = jsonStringify({ + * foo: "foo", + * bar: "bar", + * bigint: 10n + * }) + * ``` + * + * @param value + * @param space + * @returns + */ +export function jsonStringify(value: unknown, space?: number | string): string { + return JSON.stringify(_replacer(value), replacer, space); +} + +/** + * Parse a value that was stringified using `jsonStringify()` + * + * @example + * ```ts + * import { jsonStringify, jsonParse } from "@olli/kvdex" + * + * const str = jsonStringify({ + * foo: "foo", + * bar: "bar", + * bigint: 10n + * }) + * + * const value = jsonParse(str) + * ``` + * + * @param value + * @returns + */ +export function jsonParse(value: string): T { + return postReviver(JSON.parse(value, reviver)) as T; +} + +/** + * Outer replacer function. + * + * @param _key + * @param value + * @returns + */ +function replacer(_key: string, value: unknown) { + return _replacer(value); +} + +type JSONError = { + message: string; + name: string; + cause?: string; + stack?: string; +}; + +enum TypeKey { + Undefined = "__undefined__", + BigInt = "__bigint__", + KvU64 = "__kvu64__", + Int8Array = "__int8array__", + Int16Array = "__int16array__", + Int32Array = "__int32array__", + BigInt64Array = "__bigint64array__", + Uint8Array = "__uint8array__", + Uint16Array = "__uint16array__", + Uint32Array = "__uint32array__", + BigUint64Array = "__biguint64array__", + Uint8ClampedArray = "__uint8clampedarray__", + // TODO: Float16Array = "__float16array__", + Float32Array = "__float32array__", + Float64Array = "__float64array__", + ArrayBuffer = "__arraybuffer__", + Date = "__date__", + Set = "__set__", + Map = "__map__", + RegExp = "__regexp__", + DataView = "__dataview__", + Error = "__error__", + NaN = "__nan__", + Infinity = "__infinity__", +} + +/** + * Inner replacer function. + * + * @param value + * @returns + */ +function _replacer(value: unknown): unknown { + // undefined + if (value === undefined) { + return { + [TypeKey.Undefined]: false, + }; + } + + // NaN + if (Number.isNaN(value)) { + return { + [TypeKey.NaN]: false, + }; + } + + // Infinity + if (value === Infinity) { + return { + [TypeKey.Infinity]: false, + }; + } + + // bigint + if (typeof value === "bigint") { + return { + [TypeKey.BigInt]: value.toString(), + }; + } + + // Date + if (value instanceof Date) { + return { + [TypeKey.Date]: value.toISOString(), + }; + } + + // Array + if (Array.isArray(value)) { + return value.map(_replacer); + } + + // Set + if (value instanceof Set) { + return { + [TypeKey.Set]: Array.from(value.values()).map(_replacer), + }; + } + + // Map + if (value instanceof Map) { + return { + [TypeKey.Map]: Array.from(value.entries()).map(( + [k, v], + ) => [k, _replacer(v)]), + }; + } + + // RegExp + if (value instanceof RegExp) { + return { + [TypeKey.RegExp]: value.source, + }; + } + + // Error + if (value instanceof Error) { + const jsonError: JSONError = { + message: value.message, + name: value.name, + stack: value.stack, + cause: jsonStringify(value.cause), + }; + return { + [TypeKey.Error]: jsonError, + }; + } + + // Int8Array + if (value instanceof Int8Array) { + return { + [TypeKey.Int8Array]: Array.from(value), + }; + } + + // Int16Array + if (value instanceof Int16Array) { + return { + [TypeKey.Int16Array]: Array.from(value), + }; + } + + // Int32Array + if (value instanceof Int32Array) { + return { + [TypeKey.Int32Array]: Array.from(value), + }; + } + + // BigInt64Array + if (value instanceof BigInt64Array) { + return { + [TypeKey.BigInt64Array]: Array.from(value), + }; + } + + // Uint8Array + if (value instanceof Uint8Array) { + return { + [TypeKey.Uint8Array]: Array.from(value), + }; + } + + // Uint16Array + if (value instanceof Uint16Array) { + return { + [TypeKey.Uint16Array]: Array.from(value), + }; + } + + // Uint32Array + if (value instanceof Uint32Array) { + return { + [TypeKey.Uint32Array]: Array.from(value), + }; + } + + // BigUint64Array + if (value instanceof BigUint64Array) { + return { + [TypeKey.BigUint64Array]: Array.from(value), + }; + } + + // Uint8ClampedArray + if (value instanceof Uint8ClampedArray) { + return { + [TypeKey.Uint8ClampedArray]: Array.from(value), + }; + } + + // TODO: + /* + // Float16Array + if (value instanceof Float16Array) { + return { + [TypeKey.Float16Array]: Array.from(value), + }; + }*/ + + // Float32Array + if (value instanceof Float32Array) { + return { + [TypeKey.Float32Array]: Array.from(value), + }; + } + + // Float64Array + if (value instanceof Float64Array) { + return { + [TypeKey.Float64Array]: Array.from(value), + }; + } + + // ArrayBuffer + if (value instanceof ArrayBuffer) { + return { + [TypeKey.ArrayBuffer]: Array.from(new Uint8Array(value)), + }; + } + + // DataView + if (value instanceof DataView) { + return { + [TypeKey.DataView]: Array.from(new Uint8Array(value.buffer)), + }; + } + + // KvObject + if (isKvObject(value)) { + return Object.fromEntries( + Object.entries(value as KvObject).map(( + [k, v], + ) => [k, _replacer(v)]), + ); + } + + return value; +} + +/** + * Outer reviver function. + * + * @param _key + * @param value + * @returns + */ +function reviver(_key: string, value: unknown) { + return _reviver(value); +} + +/** + * Inner reviver function. + * + * @param value + * @returns + */ +function _reviver(value: unknown): unknown { + // Return if nullish or not an object + if ( + value === null || + value === undefined || + typeof value !== "object" + ) { + return value; + } + + // bigint + if (TypeKey.BigInt in value) { + return BigInt(mapValue(TypeKey.BigInt, value)); + } + + // Date + if (TypeKey.Date in value) { + return new Date(mapValue(TypeKey.Date, value)); + } + + // NaN + if (TypeKey.NaN in value) { + return NaN; + } + + // Infnity + if (TypeKey.Infinity in value) { + return Infinity; + } + + // RegExp + if (TypeKey.RegExp in value) { + return new RegExp(mapValue(TypeKey.RegExp, value)); + } + + // Error + if (TypeKey.Error in value) { + const { message, stack, cause, ...rest } = mapValue( + TypeKey.Error, + value, + ); + + const error = new Error(message, { + cause: cause ? jsonParse(cause) : undefined, + ...rest, + }); + + error.stack = stack; + return error; + } + + // Int8Array + if (TypeKey.Int8Array in value) { + return Int8Array.from(mapValue(TypeKey.Int8Array, value)); + } + + // Int16Array + if (TypeKey.Int16Array in value) { + return Int16Array.from(mapValue(TypeKey.Int16Array, value)); + } + + // Int32Array + if (TypeKey.Int32Array in value) { + return Int32Array.from(mapValue(TypeKey.Int32Array, value)); + } + + // BigInt64Array + if (TypeKey.BigInt64Array in value) { + return BigInt64Array.from(mapValue(TypeKey.BigInt64Array, value)); + } + + // Uint8Array + if (TypeKey.Uint8Array in value) { + return Uint8Array.from(mapValue(TypeKey.Uint8Array, value)); + } + + // Uint16Array + if (TypeKey.Uint16Array in value) { + return Uint16Array.from(mapValue(TypeKey.Uint16Array, value)); + } + + // Uint32Array + if (TypeKey.Uint32Array in value) { + return Uint32Array.from(mapValue(TypeKey.Uint32Array, value)); + } + + // BigUint64Array + if (TypeKey.BigUint64Array in value) { + return BigUint64Array.from(mapValue(TypeKey.BigUint64Array, value)); + } + + // Uint8ClampedArray + if (TypeKey.Uint8ClampedArray in value) { + return Uint8ClampedArray.from( + mapValue(TypeKey.Uint8ClampedArray, value), + ); + } + + // TODO: + /* + // Float16Array + if (TypeKey.Float16Array in value) { + return Float16Array.from(mapValue(TypeKey.Float16Array, value)); + }*/ + + // Float32Array + if (TypeKey.Float32Array in value) { + return Float32Array.from(mapValue(TypeKey.Float32Array, value)); + } + + // Float64Array + if (TypeKey.Float64Array in value) { + return Float64Array.from(mapValue(TypeKey.Float64Array, value)); + } + + // ArrayBuffer + if (TypeKey.ArrayBuffer in value) { + const uint8array = Uint8Array.from( + mapValue(TypeKey.ArrayBuffer, value), + ); + return uint8array.buffer; + } + + // DataView + if (TypeKey.DataView in value) { + const uint8array = Uint8Array.from(mapValue(TypeKey.DataView, value)); + return new DataView(uint8array.buffer); + } + + // Set + if (TypeKey.Set in value) { + return new Set(mapValue>(TypeKey.Set, value)); + } + + // Map + if (TypeKey.Map in value) { + return new Map(mapValue>(TypeKey.Map, value)); + } + + // Array + if (Array.isArray(value)) { + return value.map(_reviver); + } + + // KvObject + if (isKvObject(value)) { + return Object.fromEntries( + Object.entries(value).map(([k, v]) => [k, _reviver(v)]), + ); + } + + // Return value + return value; +} + +/** + * Additional revival steps to perform after initial parse. + * + * @param value + * @returns + */ +function postReviver(value: T): T { + // Return value if not an object + if ( + value === undefined || + value === null || + typeof value !== "object" + ) { + return value; + } + + // undefined + if (TypeKey.Undefined in value) { + return undefined as T; + } + + // Array + if (Array.isArray(value)) { + return value.map(postReviver) as T; + } + + // Set + if (value instanceof Set) { + return new Set( + Array.from(value.values()).map(postReviver), + ) as T; + } + + // Map + if (value instanceof Map) { + return new Map( + Array.from(value.entries()).map(([k, v]) => [k, postReviver(v)]), + ) as T; + } + + // KvObject + if (isKvObject(value)) { + return Object.fromEntries( + Object.entries(value).map(([k, v]) => [k, postReviver(v)]), + ) as T; + } + + return value; +} + +/** + * Map from special type entry to value. + * + * @param key - Type key. + * @param value - JSON value to map from. + * @returns Mapped value. + */ +function mapValue(key: string, value: unknown) { + return (value as Record)[key]; +} diff --git a/src/ext/encoding/mod.ts b/src/ext/encoding/mod.ts new file mode 100644 index 0000000..c92e1c8 --- /dev/null +++ b/src/ext/encoding/mod.ts @@ -0,0 +1,107 @@ +/** + * @module # Encoding + * + * Utilities for encoding data. + * + * @module ## JSON + * + * JSON-encoder and utilities for stringifying and serializing data. + * + * @example + * ```ts + * import { jsonEncoder } from "@olli/kvdex/encoding/json" + * + * // With default options (no compression) + * const encoder = jsonEncoder() + * ``` + * + * @example + * ```ts + * import { jsonEncoder } from "@olli/kvdex/encoding/json" + * import { brotliCompressor } from "@olli/kvdex/encoding/brotli" + * + * // With brotli compression + * const encoder = jsonEncoder({ compressor: brotliCompressor() }) + * ``` + * + * @example + * ```ts + * import { jsonStringify, jsonParse } from "@olli/kvdex/encoding/json" + * + * // Stringify value + * const json = jsonStringify({ + * foo: "bar", + * big: 100n + * }) + * + * // Parse value + * const value = jsonParse(json) + * ``` + * + * @example + * ```ts + * import { jsonSerialize, jsonDeserialize } from "@olli/kvdex/encoding/json" + * + * // Serialize value as Uint8Array + * const serialized = jsonSerialize({ + * foo: "bar", + * big: 100n + * }) + * + * // Deserialize value from Uint8Array + * const value = jsonDeserialize(serialized) + * ``` + * + * @module ## V8 + * + * V8-encoder and serialization utilities. + * + * Relies on the `node:v8` built-in. + * + * @example + * ```ts + * import { v8Encoder } from "@olli/kvdex/encoding/v8" + * import { brotliCompressor } from "@olli/kvdex/encoding/brotli" + * + * // V8-encoder without compression + * const encoder = v8Encoder() + * + * // V8-encoder with brotli compression + * const encoder = v8Encoder({ compressor: brotliCompressor() }) + * ``` + * + * @example + * ```ts + * import { v8Serialize, v8Deserialize } from "@olli/kvdex/encoding/v8" + * + * // Serialize value as Uint8Array + * const serialized = v8Serialize({ + * foo: "bar", + * big: 100n + * }) + * + * // Deserialize value from Uint8Array + * const value = v8Deserialize(serialized) + * ``` + * + * @module ## Brotli + * + * Easy to configure brotli compression for use with the `encoder` option for collections. + * + * Relies on the `node:zlib` built-in. + * + * @example + * ```ts + * import { brotliCompressor } from "@olli/kvdex/encoding/brotli" + * + * // With default options + * const compressor = brotliCompressor() + * + * // Explicitly set quality level (default is 1) + * const compressor = brotliCompressor({ quality: 2 }) + * ``` + */ + +export * from "./brotli/mod.ts"; +export * from "./json/mod.ts"; +export * from "./v8/mod.ts"; diff --git a/src/ext/encoding/v8/mod.ts b/src/ext/encoding/v8/mod.ts new file mode 100644 index 0000000..8c686d6 --- /dev/null +++ b/src/ext/encoding/v8/mod.ts @@ -0,0 +1,37 @@ +/** + * @module # V8 + * + * V8-encoder and serialization utilities. + * + * Relies on the `node:v8` built-in. + * + * @example + * ```ts + * import { v8Encoder } from "@olli/kvdex/encoding/v8" + * import { brotliCompressor } from "@olli/kvdex/encoding/brotli" + * + * // V8-encoder without compression + * const encoder = v8Encoder() + * + * // V8-encoder with brotli compression + * const encoder = v8Encoder({ compressor: brotliCompressor() }) + * ``` + * + * @example + * ```ts + * import { v8Serialize, v8Deserialize } from "@olli/kvdex/encoding/v8" + * + * // Serialize value as Uint8Array + * const serialized = v8Serialize({ + * foo: "bar", + * big: 100n + * }) + * + * // Deserialize value from Uint8Array + * const value = v8Deserialize(serialized) + * ``` + */ + +export { v8Encoder } from "./v8_encoder.ts"; +export { v8Deserialize, v8Serialize } from "./utils.ts"; +export type * from "./types.ts"; diff --git a/src/ext/encoding/v8/types.ts b/src/ext/encoding/v8/types.ts new file mode 100644 index 0000000..db85c07 --- /dev/null +++ b/src/ext/encoding/v8/types.ts @@ -0,0 +1,7 @@ +import type { Compressor } from "../../../types.ts"; + +/** Options for V8 encoding. */ +export type V8EncoderOptions = { + /** Optional compressor object. */ + compressor?: Compressor; +}; diff --git a/src/ext/encoding/v8/utils.ts b/src/ext/encoding/v8/utils.ts new file mode 100644 index 0000000..62e92b3 --- /dev/null +++ b/src/ext/encoding/v8/utils.ts @@ -0,0 +1,112 @@ +import type { KvObject } from "../../../types.ts"; +import { isKvObject } from "../../../utils.ts"; +import { deserialize, serialize } from "node:v8"; + +/** + * Extended V8 serialize. + * + * @param value - Value to be serialized. + * @returns A serialized value. + */ +export function v8Serialize(value: unknown): Uint8Array { + return new Uint8Array(serialize(beforeV8Serialize(value))); +} + +/** + * Extended V8 deserialize. + * + * @param value - Value to be deserialized. + * @returns Deserialized value. + */ +export function v8Deserialize( + value: Uint8Array, +): T { + return afterV8Serialize(deserialize(value)) as T; +} + +/** + * Additional steps to perform before V8 serialize. + * + * @param value + * @returns + */ +function beforeV8Serialize(value: unknown): unknown { + // KvObject + if (isKvObject(value)) { + return Object.fromEntries( + Object.entries(value as KvObject).map(( + [key, val], + ) => [key, beforeV8Serialize(val)]), + ); + } + + // Array + if (Array.isArray(value)) { + return value.map((val) => beforeV8Serialize(val)); + } + + // Set + if (value instanceof Set) { + return new Set( + Array.from(value.values()).map((v) => beforeV8Serialize(v)), + ); + } + + // Map + if (value instanceof Map) { + return new Map( + Array.from(value.entries()).map(( + [k, v], + ) => [k, beforeV8Serialize(v)]), + ); + } + + return value; +} + +/** + * Additional steps to perform after V8 deserialize. + * + * @param value + * @returns + */ +function afterV8Serialize(value: unknown): unknown { + // Return value if not an object + if ( + value === undefined || + value === null || + typeof value !== "object" + ) { + return value; + } + + // KvObject + if (isKvObject(value)) { + return Object.fromEntries( + Object.entries(value).map(([k, v]) => [k, afterV8Serialize(v)]), + ); + } + + // Array + if (Array.isArray(value)) { + return value.map((v) => afterV8Serialize(v)); + } + + // Set + if (value instanceof Set) { + return new Set( + Array.from(value.values()).map((v) => afterV8Serialize(v)), + ); + } + + // Map + if (value instanceof Map) { + return new Map( + Array.from(value.entries()).map(( + [k, v], + ) => [k, afterV8Serialize(v)]), + ); + } + + return value; +} diff --git a/src/ext/encoding/v8/v8_encoder.ts b/src/ext/encoding/v8/v8_encoder.ts new file mode 100644 index 0000000..3d2b114 --- /dev/null +++ b/src/ext/encoding/v8/v8_encoder.ts @@ -0,0 +1,23 @@ +import type { Encoder } from "../../../types.ts"; +import type { V8EncoderOptions } from "./types.ts"; +import { v8Deserialize, v8Serialize } from "./utils.ts"; + +/** + * V8-encoder. + * + * Used for serializing and deserializing data as Uint8Array. + * + * Relies on `serialize()` and `deserialize()` from the node:v8 built-in module. + * + * @param options - V8 encoding options. + * @returns - An Encoder object. + */ +export function v8Encoder(options?: V8EncoderOptions): Encoder { + return { + serializer: { + serialize: v8Serialize, + deserialize: v8Deserialize, + }, + compressor: options?.compressor, + }; +} diff --git a/src/ext/kv/map_kv.ts b/src/ext/kv/map_kv.ts index a0be60c..6a93af6 100644 --- a/src/ext/kv/map_kv.ts +++ b/src/ext/kv/map_kv.ts @@ -13,11 +13,11 @@ import type { DenoKvStrictKey, DenoKvWatchOptions, } from "../../types.ts"; -import { jsonParse, jsonStringify } from "../../utils.ts"; import { MapKvAtomicOperation } from "./atomic.ts"; import { Watcher } from "./watcher.ts"; import { createVersionstamp, keySort } from "./utils.ts"; import type { BasicMap, MapKvOptions } from "./types.ts"; +import { jsonParse, jsonStringify } from "../encoding/mod.ts"; /** * KV instance utilising a `BasicMap` as it's backend. diff --git a/src/ext/kv/mod.ts b/src/ext/kv/mod.ts index cf42941..9eef711 100644 --- a/src/ext/kv/mod.ts +++ b/src/ext/kv/mod.ts @@ -13,7 +13,7 @@ * * // Create a database from a `MapKv` instance, using `Map` as it's backend by default. * const kv = new MapKv() - * const db = kvdex(kv, {}) + * const db = kvdex({ kv }) * ``` * * @example @@ -23,7 +23,7 @@ * * // Create a database from a `MapKv` instance, explicitly using `Map` as it's backend. * const kv = new MapKv({ map: new Map() }) - * const db = kvdex(kv, {}) + * const db = kvdex({ kv }) * ``` * * @example @@ -34,7 +34,7 @@ * // Create a database from a `MapKv` instance, using `localStorage` as it's backend by default. * const map = new StorageAdapter() * const kv = new MapKv({ map }) - * const db = kvdex(kv, {}) + * const db = kvdex({ kv }) * ``` * * @example @@ -45,7 +45,7 @@ * // Create an ephimeral database from a `MapKv` instance, explicitly using `localStorage` as it's backend. * const map = new StorageAdapter(localStorage) * const kv = new MapKv({ map, clearOnClose: true }) - * const db = kvdex(kv, {}) + * const db = kvdex({ kv }) * ``` */ diff --git a/src/ext/kv/storage_adapter.ts b/src/ext/kv/storage_adapter.ts index 39409ab..40bb927 100644 --- a/src/ext/kv/storage_adapter.ts +++ b/src/ext/kv/storage_adapter.ts @@ -1,4 +1,4 @@ -import { jsonParse, jsonStringify } from "../../utils.ts"; +import { jsonParse, jsonStringify } from "../encoding/mod.ts"; import type { BasicMap } from "./types.ts"; /** diff --git a/src/ext/kv/utils.ts b/src/ext/kv/utils.ts index c62e6b8..4614d9c 100644 --- a/src/ext/kv/utils.ts +++ b/src/ext/kv/utils.ts @@ -1,4 +1,4 @@ -import { ulid } from "../../deps.ts"; +import { ulid } from "@std/ulid"; import type { DenoKvStrictKey, DenoKvStrictKeyPart } from "../../types.ts"; export function createVersionstamp() { diff --git a/src/ext/kv/watcher.ts b/src/ext/kv/watcher.ts index a75ecff..2e43f22 100644 --- a/src/ext/kv/watcher.ts +++ b/src/ext/kv/watcher.ts @@ -1,6 +1,6 @@ import type { DenoKvWatchOptions } from "../../../mod.ts"; import type { DenoKvEntryMaybe, DenoKvStrictKey } from "../../types.ts"; -import { jsonStringify } from "../../utils.ts"; +import { jsonStringify } from "../encoding/mod.ts"; import type { MapKv } from "./map_kv.ts"; export class Watcher { diff --git a/src/ext/migrate/deps.ts b/src/ext/migrate/deps.ts deleted file mode 100644 index 69bcfe6..0000000 --- a/src/ext/migrate/deps.ts +++ /dev/null @@ -1 +0,0 @@ -export { parseArgs } from "jsr:@std/cli@^0.220/parse_args"; diff --git a/src/ext/migrate/migrate.ts b/src/ext/migrate/migrate.ts index 23a1536..975febd 100644 --- a/src/ext/migrate/migrate.ts +++ b/src/ext/migrate/migrate.ts @@ -6,7 +6,7 @@ import type { MigrateOptions } from "./types.ts"; * * @example * ```ts - * import { migrate } from "jsr:@olli/kvdex/ext/migrate" + * import { migrate } from "@olli/kvdex/ext/migrate" * * const source = await Deno.openKv("./source.sqlite3") * const target = await Deno.openKv("./target.sqlite3") diff --git a/src/ext/migrate/mod.ts b/src/ext/migrate/mod.ts index fd69e80..0594d6d 100644 --- a/src/ext/migrate/mod.ts +++ b/src/ext/migrate/mod.ts @@ -20,7 +20,7 @@ * Optionally pass `all: true` to migrate all entries. * * ```ts - * import { migrate } from "jsr:@olli/kvdex/migrate" + * import { migrate } from "@olli/kvdex/migrate" * * const source = await Deno.openKv("./source.sqlite3") * const target = await Deno.openKv("./target.sqlite3") @@ -33,7 +33,7 @@ */ // Imports -import { parseArgs } from "./deps.ts"; +import { parseArgs } from "@std/cli/parse-args"; import { migrate } from "./migrate.ts"; import { NoKvFoundError } from "./errors.ts"; diff --git a/src/ext/zod/deps.ts b/src/ext/zod/deps.ts deleted file mode 100644 index a4d628b..0000000 --- a/src/ext/zod/deps.ts +++ /dev/null @@ -1 +0,0 @@ -export { z } from "npm:zod@^3.22"; diff --git a/src/ext/zod/mod.ts b/src/ext/zod/mod.ts index 085825b..c7951b0 100644 --- a/src/ext/zod/mod.ts +++ b/src/ext/zod/mod.ts @@ -11,7 +11,7 @@ * * ```ts * import { z } from "npm:zod" - * import { KvIdSchema } from "jsr:@olli/kvdex/zod" + * import { KvIdSchema } from "@olli/kvdex/zod" * * const UserSchema = z.object({ * username: z.string(), diff --git a/src/ext/zod/schemas.ts b/src/ext/zod/schemas.ts index a62c4d3..f6d1205 100644 --- a/src/ext/zod/schemas.ts +++ b/src/ext/zod/schemas.ts @@ -1,4 +1,4 @@ -import { z } from "./deps.ts"; +import { z } from "zod"; import type { KvArray, KvId, KvObject, KvValue } from "../../types.ts"; const LazyKvValueSchema = z.lazy(() => KvValueSchema); @@ -33,7 +33,7 @@ export const KvValueSchema: z.ZodType = z.undefined() .or(z.instanceof(Uint32Array)) .or(z.instanceof(BigUint64Array)) .or(z.instanceof(Uint8ClampedArray)) - .or(z.instanceof(Float16Array)) + // TODO: .or(z.instanceof(Float16Array)) .or(z.instanceof(Float32Array)) .or(z.instanceof(Float64Array)) .or(z.instanceof(ArrayBuffer)) diff --git a/src/kvdex.ts b/src/kvdex.ts index 3aa8f81..29dde2c 100644 --- a/src/kvdex.ts +++ b/src/kvdex.ts @@ -9,6 +9,7 @@ import type { FindOptions, IntervalMessage, IntervalSetter, + KvdexOptions, KvId, KvKey, KvValue, @@ -48,7 +49,8 @@ import { AtomicWrapper } from "./atomic_wrapper.ts"; * * @example * ```ts - * import { kvdex, model, collection } from "jsr:@olli/kvdex" + * import { kvdex, model, collection } from "@olli/kvdex" + * import { jsonEncoder } from "@olli/kvdex" * * type User = { * username: string @@ -57,16 +59,19 @@ import { AtomicWrapper } from "./atomic_wrapper.ts"; * * const kv = await Deno.openKv() * - * const db = kvdex(kv, { - * numbers: collection(model()), - * u64s: collection(model()), - * serializedStrings: collection(model(), { serialize: "json" }), - * users: collection(model(), { - * indices: { - * username: "primary", - * age: "secondary" - * } - * }) + * const db = kvdex({ + * kv: kv, + * schema: { + * numbers: collection(model()), + * u64s: collection(model()), + * serializedStrings: collection(model(), { encoder: jsonEncoder() }), + * users: collection(model(), { + * indices: { + * username: "primary", + * age: "secondary" + * } + * }) + * } * }) * ``` * @@ -74,10 +79,9 @@ import { AtomicWrapper } from "./atomic_wrapper.ts"; * @param schemaDefinition - The schema definition used to build collections and create the database schema. * @returns A Kvdex instance with attached schema. */ -export function kvdex( - kv: DenoKv, - schemaDefinition: T, -): Kvdex> & Schema { +export function kvdex( + options: T, +): Kvdex> & Schema { // Set listener activated flag and queue handlers map let listener: Promise; const queueHandlers = new Map[]>(); @@ -87,7 +91,7 @@ export function kvdex( // Create new queue listener if not already created if (!listener) { // Add queue listener - listener = kv.listenQueue(async (msg) => { + listener = options.kv.listenQueue(async (msg) => { // Parse queue message const parsed = parseQueueMessage(msg); if (!parsed.ok) { @@ -109,14 +113,19 @@ export function kvdex( // Create schema const schema = _createSchema( - schemaDefinition, - kv, + options.schema ?? {}, + options.kv, queueHandlers, idempotentListener, - ) as Schema; + ) as Schema; // Create KvDex object - const db = new Kvdex(kv, schema, queueHandlers, idempotentListener); + const db = new Kvdex( + options.kv, + schema, + queueHandlers, + idempotentListener, + ); // Return schema and db combination return Object.assign(db, schema); @@ -588,13 +597,13 @@ export class Kvdex> { * @param treeKey - The current tree key. * @returns */ -function _createSchema( - def: T, +function _createSchema( + def: SchemaDefinition, kv: DenoKv, queueHandlers: Map[]>, idempotentListener: () => Promise, treeKey?: KvKey, -): Schema { +): Schema { // Get all the definition entries const entries = Object.entries(def); @@ -619,7 +628,7 @@ function _createSchema( const schema = Object.fromEntries(schemaEntries); // Return the built schema object - return schema as Schema; + return schema; } /** diff --git a/src/types.ts b/src/types.ts index 659819d..08ea624 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,5 +1,5 @@ import type { Collection } from "./collection.ts"; -import type { DeepMergeOptions } from "./deps.ts"; +import type { DeepMergeOptions } from "@std/collections/deep-merge"; import type { Document } from "./document.ts"; /*********************/ @@ -8,6 +8,11 @@ import type { Document } from "./document.ts"; /* */ /*********************/ +const EMPTY_OBJECT = {}; + +/** Empty object type */ +export type EmptyObject = typeof EMPTY_OBJECT; + /** Collection builder function */ export type BuilderFn< TInput, @@ -66,6 +71,15 @@ export type IdGenerator = ( data: T1, ) => T2 | Promise; +/** Management of an active watcher. */ +export type WatchManager = { + /** The watcher promise, which is resolved either by closing the KV connection or by calling the watcher's `cancel()` function. */ + promise: Promise; + + /** Stops the active watcher. */ + cancel: () => Promise; +}; + /**********************/ /* */ /* INTERVAL TYPES */ @@ -261,9 +275,10 @@ export type AtomicMutation = ); /** Options for atomic set operation */ -export type AtomicSetOptions = NonNullable< - Parameters["set"]>["2"] ->; +export type AtomicSetOptions> = + & DenoKvSetOptions + & (T extends { indices: IndexRecord } ? EmptyObject + : Pick); /************************/ /* */ @@ -275,7 +290,7 @@ export type AtomicSetOptions = NonNullable< export type CollectionOptions = & { idGenerator?: IdGenerator; - serialize?: SerializeOptions; + encoder?: Encoder; history?: true; } & ( @@ -373,50 +388,51 @@ export type IndexDataEntry = Omit & { __id__: KvId; }; -/***********************/ -/* */ -/* SERIALIZE TYPES */ -/* */ -/***********************/ +/**********************/ +/* */ +/* ENCODING TYPES */ +/* */ +/**********************/ + +/** + * Object containing logic for serialization and compression of KvValues. + * + * Implements a serializer, and optionally a compressor. + */ +export type Encoder = { + serializer: Serializer; + compressor?: Compressor; +}; -/** Record of serializer functions */ +/** Object that implements a serilize and deserilaize method */ export type Serializer = { - serialize: (data: T) => Uint8Array | Promise; - deserialize: (data: Uint8Array) => T | Promise; - compress: (data: Uint8Array) => Uint8Array | Promise; - decompress: (data: Uint8Array) => Uint8Array | Promise; + serialize: SerializeFn; + deserialize: DeserializeFn; }; -/** Serialized value entry */ -export type SerializedEntry = { +/** Object that implements a compress and decompress method */ +export type Compressor = { + compress: CompressFn; + decompress: CompressFn; +}; + +/** Function that serializes a KvValue as a Uint8Array */ +export type SerializeFn = (data: unknown) => Uint8Array | Promise; + +/** Function that deserializes a KvValue from a Uint8Array */ +export type DeserializeFn = ( + data: Uint8Array, +) => T | Promise; + +/** Function that compresses/decompresses data represented by a Uint8Array */ +export type CompressFn = (data: Uint8Array) => Uint8Array | Promise; + +/** Encoded value entry */ +export type EncodedEntry = { isUint8Array: boolean; ids: KvId[]; }; -/** - * Serialize options. - * - * "v8" = built-in v8 serializer + brotli compression. - * - * "v8-uncompressed" = built-in v8 serializer and no compression. - * - * "json" = custom JSON serializer + brotli compression. - * - * "json-uncompressed" = custom JSON serializer and no compression (best runtime compatibility). - * - * If the serialize option is not set, or if a custom serialize configuration is used, - * then JSON serialization is used by default for any unset serialize functions, - * while brotli compression is used for any unset compress functions. V8 serialization and - * Brotli compression rely on runtime implementations, and are therefore only - * compatible with runtimes that implement them (Deno, Node.js). - */ -export type SerializeOptions = - | "v8" - | "v8-uncompressed" - | "json" - | "json-uncompressed" - | Partial; - /***************************/ /* */ /* METHOD OPTION TYPES */ @@ -596,11 +612,11 @@ export type PrimaryIndexUpsert< update: UpdateData; }; -/********************/ -/* */ -/* SCHEMA TYPES */ -/* */ -/********************/ +/*******************/ +/* */ +/* KVDEX TYPES */ +/* */ +/*******************/ /** Schema definition, containing builder functions and nested schema definitions. */ export type SchemaDefinition = { @@ -610,10 +626,21 @@ export type SchemaDefinition = { }; /** Built schema from schema definition */ -export type Schema = { - [K in keyof T]: T[K] extends SchemaDefinition ? Schema - : T[K] extends BuilderFnAny ? ReturnType - : never; +export type Schema = T extends undefined + ? EmptyObject + : { + [K in keyof T]: T[K] extends SchemaDefinition ? Schema + : T[K] extends BuilderFnAny ? ReturnType + : never; + }; + +/** Database options */ +export type KvdexOptions = { + /** The KV instance that will power the database. */ + kv: DenoKv; + + /** Schema definition containing the database collections */ + schema?: SchemaDefinition; }; /*******************/ @@ -722,7 +749,7 @@ export type KvValue = | Uint32Array | BigUint64Array | Uint8ClampedArray - | Float16Array + // TODO: | Float16Array | Float32Array | Float64Array | ArrayBuffer diff --git a/src/utils.ts b/src/utils.ts index b4cde2b..2524b38 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,12 +1,13 @@ -import { COMPRESSION_QUALITY_LEVEL, GET_MANY_KEY_LIMIT } from "./constants.ts"; +import { GET_MANY_KEY_LIMIT } from "./constants.ts"; import type { Collection } from "./collection.ts"; import type { - AtomicSetOptions, DenoAtomicOperation, DenoKv, DenoKvEntryMaybe, DenoKvListSelector, + DenoKvSetOptions, DenoKvStrictKey, + Encoder, EnqueueOptions, FindManyOptions, IndexDataEntry, @@ -18,22 +19,11 @@ import type { ParsedQueueMessage, PreparedEnqueue, QueueMessage, + WatchManager, WatchOptions, } from "./types.ts"; -import { - brotliCompressSync, - brotliDecompressSync, - constants, - deserialize as _v8Deserialize, - serialize as _v8Serialize, - ulid, -} from "./deps.ts"; - -/*************************/ -/* */ -/* PUBLIC FUNCTIONS */ -/* */ -/*************************/ +import { ulid } from "@std/ulid"; +import { jsonEncoder } from "./ext/encoding/mod.ts"; /** * Generate a new document id. @@ -76,6 +66,24 @@ export function keyEq(k1: KvKey, k2: KvKey) { return JSON.stringify(k1) === JSON.stringify(k2); } +export async function encodeData( + value: unknown, + encoder: Encoder = jsonEncoder(), +): Promise { + const { serializer, compressor } = encoder; + const serialized = await serializer.serialize(value); + return compressor ? await compressor.compress(serialized) : serialized; +} + +export async function decodeData( + value: Uint8Array, + encoder: Encoder = jsonEncoder(), +): Promise { + const { serializer, compressor } = encoder; + const decompressed = compressor ? await compressor.decompress(value) : value; + return await serializer.deserialize(decompressed); +} + /** * Create a secondary index key prefix. * @@ -90,14 +98,13 @@ export async function createSecondaryIndexKeyPrefix( collection: Collection, ) { // Serialize and compress index value - const serialized = await collection._serializer.serialize(value); - const compressed = await collection._serializer.compress(serialized); + const encoded = await encodeData(value, collection._encoder); // Create prefix key return extendKey( collection._keys.secondaryIndex, index as KvId, - compressed, + encoded, ); } @@ -130,7 +137,7 @@ export function isKvObject(value: unknown) { value instanceof Uint32Array || value instanceof BigUint64Array || value instanceof Uint8ClampedArray || - value instanceof Float16Array || + // TODO: value instanceof Float16Array || value instanceof Float32Array || value instanceof Float64Array || value instanceof ArrayBuffer || @@ -164,59 +171,27 @@ export async function setIndices( value: KvObject, atomic: DenoAtomicOperation, collection: Collection, - options: AtomicSetOptions | undefined, + options: DenoKvSetOptions | undefined, ) { - // Set primary indices using primary index list - for (const index of collection._primaryIndexList) { - // Get the index value from data, if undefined continue to next index - const indexValue = data[index] as KvId | undefined; - if (typeof indexValue === "undefined") continue; - - // Serialize and compress - const serialized = await collection._serializer.serialize(indexValue); - const compressed = await collection._serializer.compress(serialized); - - // Create the index key - const indexKey = extendKey( - collection._keys.primaryIndex, - index, - compressed, - ); - - // Create the index document value - const indexEntry: IndexDataEntry = { - ...value, - __id__: id, - }; - - // Add index insertion to atomic operation, check for exisitng indices - atomic.set(indexKey, indexEntry, options).check({ - key: indexKey, - versionstamp: null, - }); - } - - // Set secondary indices using secondary index list - for (const index of collection._secondaryIndexList) { - // Get the index value from data, if undefined continue to next index - const indexValue = data[index] as KvId | undefined; - if (typeof indexValue === "undefined") continue; - - // Serialize and compress - const serialized = await collection._serializer.serialize(indexValue); - const compressed = await collection._serializer.compress(serialized); - - // Create the index key - const indexKey = extendKey( - collection._keys.secondaryIndex, - index, - compressed, - id, - ); - - // Add index insertion to atomic operation, check for exisitng indices - atomic.set(indexKey, value, options); - } + await handleIndices( + id, + data, + collection, + (primaryIndexKey) => { + const indexEntry: IndexDataEntry = { + ...value, + __id__: id, + }; + + atomic.set(primaryIndexKey, indexEntry, options).check({ + key: primaryIndexKey, + versionstamp: null, + }); + }, + (secondaryIndexKey) => { + atomic.set(secondaryIndexKey, value, options); + }, + ); } /** @@ -232,31 +207,17 @@ export async function checkIndices( atomic: DenoAtomicOperation, collection: Collection, ) { - // Check primary indices using primary index list - for (const index of collection._primaryIndexList) { - // Get the index value from data, if undefined continue to next index - const indexValue = data[index] as KvId | undefined; - if (typeof indexValue === "undefined") { - continue; - } - - // Serialize and compress - const serialized = await collection._serializer.serialize(indexValue); - const compressed = await collection._serializer.compress(serialized); - - // Create the index key - const indexKey = extendKey( - collection._keys.primaryIndex, - index, - compressed, - ); - - // Check for existing index entry - atomic.check({ - key: indexKey, - versionstamp: null, - }); - } + await handleIndices( + null, + data, + collection, + (primaryIndexKey) => { + atomic.check({ + key: primaryIndexKey, + versionstamp: null, + }); + }, + ); } /** @@ -274,48 +235,13 @@ export async function deleteIndices( atomic: DenoAtomicOperation, collection: Collection, ) { - // Delete primary indices using primary index list - for (const index of collection._primaryIndexList) { - // Get the index value from data, if undefined continue to next index - const indexValue = data[index] as KvId | undefined; - if (typeof indexValue === "undefined") continue; - - // Serialize and compress - const serialized = await collection._serializer.serialize(indexValue); - const compressed = await collection._serializer.compress(serialized); - - // Create the index key - const indexKey = extendKey( - collection._keys.primaryIndex, - index, - compressed, - ); - - // Add index deletion to atomic operation - atomic.delete(indexKey); - } - - // Delete seocndary indices using secondary index list - for (const index of collection._secondaryIndexList) { - // Get the index value from data, if undefined continue to next index - const indexValue = data[index] as KvId | undefined; - if (typeof indexValue === "undefined") continue; - - // Serialize and compress - const serialized = await collection._serializer.serialize(indexValue); - const compressed = await collection._serializer.compress(serialized); - - // Create the index key - const indexKey = extendKey( - collection._keys.secondaryIndex, - index, - compressed, - id, - ); - - // Add index deletion to atomic operation - atomic.delete(indexKey); - } + await handleIndices( + id, + data, + collection, + (primaryIndexKey) => atomic.delete(primaryIndexKey), + (secondaryIndexKey) => atomic.delete(secondaryIndexKey), + ); } /** @@ -530,10 +456,7 @@ export function createWatcher( options: WatchOptions | undefined, keys: KvKey[], fn: (entries: DenoKvEntryMaybe[]) => unknown, -): { - promise: Promise; - cancel: () => Promise; -} { +): WatchManager { // Create watch stream const stream = kv.watch(keys, options); const reader = stream.getReader(); @@ -564,693 +487,47 @@ export function createWatcher( return { promise: promise(), cancel }; } -/** - * Compress a uint8Array. - * - * @param data - Uint8Array to be compressed. - * @returns Compressed Uint8Array. - */ -export function compress(data: Uint8Array) { - const buffer = brotliCompressSync(data, { - params: { [constants.BROTLI_PARAM_QUALITY]: COMPRESSION_QUALITY_LEVEL }, - }); - - return new Uint8Array(buffer); -} - -/** - * Decompress a Uint8Array. - * - * @param data - Uint8Array to be decompressed. - * @returns Decompressed Uint8Array. - */ -export function decompress(data: Uint8Array) { - const buffer = brotliDecompressSync(data, { - params: { [constants.BROTLI_PARAM_QUALITY]: COMPRESSION_QUALITY_LEVEL }, - }); - - return new Uint8Array(buffer); -} - -/** - * Extended V8 serialize. - * - * @param value - Value to be serialized. - * @returns A serialized value. - */ -export function v8Serialize(value: unknown): Uint8Array { - return new Uint8Array(_v8Serialize(beforeV8Serialize(value))); -} - -/** - * Extended V8 deserialize. - * - * @param value - Value to be deserialized. - * @returns Deserialized value. - */ -export function v8Deserialize( - value: Uint8Array, -): T { - return afterV8Serialize(_v8Deserialize(value)) as T; -} - -export type JSONError = { - message: string; - name: string; - cause?: string; - stack?: string; -}; - -export enum TypeKey { - Undefined = "__undefined__", - BigInt = "__bigint__", - KvU64 = "__kvu64__", - Int8Array = "__int8array__", - Int16Array = "__int16array__", - Int32Array = "__int32array__", - BigInt64Array = "__bigint64array__", - Uint8Array = "__uint8array__", - Uint16Array = "__uint16array__", - Uint32Array = "__uint32array__", - BigUint64Array = "__biguint64array__", - Uint8ClampedArray = "__uint8clampedarray__", - Float16Array = "__float16array__", - Float32Array = "__float32array__", - Float64Array = "__float64array__", - ArrayBuffer = "__arraybuffer__", - Date = "__date__", - Set = "__set__", - Map = "__map__", - RegExp = "__regexp__", - DataView = "__dataview__", - Error = "__error__", - NaN = "__nan__", - Infinity = "__infinity__", -} - -/** - * Additional steps to perform before V8 serialize. - * - * @param value - * @returns - */ -export function beforeV8Serialize(value: unknown): unknown { - // KvObject - if (isKvObject(value)) { - return Object.fromEntries( - Object.entries(value as KvObject).map(( - [key, val], - ) => [key, beforeV8Serialize(val)]), - ); - } - - // Array - if (Array.isArray(value)) { - return value.map((val) => beforeV8Serialize(val)); - } - - // Set - if (value instanceof Set) { - return new Set( - Array.from(value.values()).map((v) => beforeV8Serialize(v)), - ); - } - - // Map - if (value instanceof Map) { - return new Map( - Array.from(value.entries()).map(( - [k, v], - ) => [k, beforeV8Serialize(v)]), - ); - } - - return value; -} - -/** - * Additional steps to perform after V8 deserialize. - * - * @param value - * @returns - */ -export function afterV8Serialize(value: unknown): unknown { - // Return value if not an object - if ( - value === undefined || - value === null || - typeof value !== "object" - ) { - return value; - } - - // KvObject - if (isKvObject(value)) { - return Object.fromEntries( - Object.entries(value).map(([k, v]) => [k, afterV8Serialize(v)]), - ); - } - - // Array - if (Array.isArray(value)) { - return value.map((v) => afterV8Serialize(v)); - } - - // Set - if (value instanceof Set) { - return new Set( - Array.from(value.values()).map((v) => afterV8Serialize(v)), - ); - } - - // Map - if (value instanceof Map) { - return new Map( - Array.from(value.entries()).map(( - [k, v], - ) => [k, afterV8Serialize(v)]), - ); - } - - return value; -} - -/** - * Serialize a JSON-like value to a Uint8Array. - * - * @example - * ```ts - * import { jsonSerialize } from "@olli/kvdex" - * - * const serialized = jsonSerialize({ - * foo: "foo", - * bar: "bar", - * bigint: 10n - * }) - * ``` - * - * @param value - Value to be serialized. - * @returns Serialized value. - */ -export function jsonSerialize(value: unknown): Uint8Array { - const str = jsonStringify(value); - return new TextEncoder().encode(str); -} - -/** - * Deserialize a value that was serialized using `jsonSerialize()`. - * - * @example - * ```ts - * import { jsonSerialize, jsonDeserialize } from "@olli/kvdex" - * - * const serialized = jsonSerialize({ - * foo: "foo", - * bar: "bar", - * bigint: 10n - * }) - * - * const value = jsonDeserialize(serialized) - * ``` - * - * @param value - Value to be deserialize. - * @returns Deserialized value. - */ -export function jsonDeserialize(value: Uint8Array): T { - const str = new TextDecoder().decode(value); - return jsonParse(str); -} - -/** - * Stringify a JSON-like value. - * - * @example - * ```ts - * import { jsonStringify } from "@olli/kvdex" - * - * const str = jsonStringify({ - * foo: "foo", - * bar: "bar", - * bigint: 10n - * }) - * ``` - * - * @param value - * @param space - * @returns - */ -export function jsonStringify(value: unknown, space?: number | string): string { - return JSON.stringify(_replacer(value), replacer, space); -} - -/** - * Parse a value that was stringified using `jsonStringify()` - * - * @example - * ```ts - * import { jsonStringify, jsonParse } from "@olli/kvdex" - * - * const str = jsonStringify({ - * foo: "foo", - * bar: "bar", - * bigint: 10n - * }) - * - * const value = jsonParse(str) - * ``` - * - * @param value - * @returns - */ -export function jsonParse(value: string): T { - return postReviver(JSON.parse(value, reviver)) as T; -} - -/** - * Outer replacer function. - * - * @param _key - * @param value - * @returns - */ -export function replacer(_key: string, value: unknown) { - return _replacer(value); -} - -/** - * Outer reviver function. - * - * @param _key - * @param value - * @returns - */ -export function reviver(_key: string, value: unknown) { - return _reviver(value); -} - -/** - * Inner replacer function. - * - * @param value - * @returns - */ -export function _replacer(value: unknown): unknown { - // undefined - if (value === undefined) { - return { - [TypeKey.Undefined]: false, - }; - } - - // NaN - if (Number.isNaN(value)) { - return { - [TypeKey.NaN]: false, - }; - } - - // Infinity - if (value === Infinity) { - return { - [TypeKey.Infinity]: false, - }; - } - - // bigint - if (typeof value === "bigint") { - return { - [TypeKey.BigInt]: value.toString(), - }; - } - - // Date - if (value instanceof Date) { - return { - [TypeKey.Date]: value.toISOString(), - }; - } - - // Array - if (Array.isArray(value)) { - return value.map(_replacer); - } - - // Set - if (value instanceof Set) { - return { - [TypeKey.Set]: Array.from(value.values()).map(_replacer), - }; - } - - // Map - if (value instanceof Map) { - return { - [TypeKey.Map]: Array.from(value.entries()).map(( - [k, v], - ) => [k, _replacer(v)]), - }; - } - - // RegExp - if (value instanceof RegExp) { - return { - [TypeKey.RegExp]: value.source, - }; - } - - // Error - if (value instanceof Error) { - const jsonError: JSONError = { - message: value.message, - name: value.name, - stack: value.stack, - cause: jsonStringify(value.cause), - }; - return { - [TypeKey.Error]: jsonError, - }; - } - - // Int8Array - if (value instanceof Int8Array) { - return { - [TypeKey.Int8Array]: Array.from(value), - }; - } - - // Int16Array - if (value instanceof Int16Array) { - return { - [TypeKey.Int16Array]: Array.from(value), - }; - } - - // Int32Array - if (value instanceof Int32Array) { - return { - [TypeKey.Int32Array]: Array.from(value), - }; - } - - // BigInt64Array - if (value instanceof BigInt64Array) { - return { - [TypeKey.BigInt64Array]: Array.from(value), - }; - } - - // Uint8Array - if (value instanceof Uint8Array) { - return { - [TypeKey.Uint8Array]: Array.from(value), - }; - } - - // Uint16Array - if (value instanceof Uint16Array) { - return { - [TypeKey.Uint16Array]: Array.from(value), - }; - } - - // Uint32Array - if (value instanceof Uint32Array) { - return { - [TypeKey.Uint32Array]: Array.from(value), - }; - } - - // BigUint64Array - if (value instanceof BigUint64Array) { - return { - [TypeKey.BigUint64Array]: Array.from(value), - }; - } - - // Uint8ClampedArray - if (value instanceof Uint8ClampedArray) { - return { - [TypeKey.Uint8ClampedArray]: Array.from(value), - }; - } - - // Float16Array - if (value instanceof Float16Array) { - return { - [TypeKey.Float16Array]: Array.from(value), - }; - } - - // Float32Array - if (value instanceof Float32Array) { - return { - [TypeKey.Float32Array]: Array.from(value), - }; - } - - // Float64Array - if (value instanceof Float64Array) { - return { - [TypeKey.Float64Array]: Array.from(value), - }; - } - - // ArrayBuffer - if (value instanceof ArrayBuffer) { - return { - [TypeKey.ArrayBuffer]: Array.from(new Uint8Array(value)), - }; - } - - // DataView - if (value instanceof DataView) { - return { - [TypeKey.DataView]: Array.from(new Uint8Array(value.buffer)), - }; - } - - // KvObject - if (isKvObject(value)) { - return Object.fromEntries( - Object.entries(value as KvObject).map(([k, v]) => [k, _replacer(v)]), - ); - } - - return value; -} - -/** - * Inner reviver function. - * - * @param value - * @returns - */ -export function _reviver(value: unknown): unknown { - // Return if nullish or not an object - if ( - value === null || - value === undefined || - typeof value !== "object" - ) { - return value; - } - - // bigint - if (TypeKey.BigInt in value) { - return BigInt(mapValue(TypeKey.BigInt, value)); - } - - // Date - if (TypeKey.Date in value) { - return new Date(mapValue(TypeKey.Date, value)); - } - - // NaN - if (TypeKey.NaN in value) { - return NaN; - } - - // Infnity - if (TypeKey.Infinity in value) { - return Infinity; - } +async function handleIndices( + id: KvId | null, + data: KvObject, + collection: Collection, + primary: (indexKey: KvKey) => void, + secondary?: (indexKey: KvKey) => void, +): Promise { + // Handle primary indices + for (const index of collection._primaryIndexList) { + const indexValue = data[index] as KvId | undefined; + if (typeof indexValue === "undefined") continue; - // RegExp - if (TypeKey.RegExp in value) { - return new RegExp(mapValue(TypeKey.RegExp, value)); - } + const encoded = await encodeData(indexValue, collection._encoder); - // Error - if (TypeKey.Error in value) { - const { message, stack, cause, ...rest } = mapValue( - TypeKey.Error, - value, + const indexKey = extendKey( + collection._keys.primaryIndex, + index, + encoded, ); - const error = new Error(message, { - cause: cause ? jsonParse(cause) : undefined, - ...rest, - }); - - error.stack = stack; - return error; - } - - // Int8Array - if (TypeKey.Int8Array in value) { - return Int8Array.from(mapValue(TypeKey.Int8Array, value)); - } - - // Int16Array - if (TypeKey.Int16Array in value) { - return Int16Array.from(mapValue(TypeKey.Int16Array, value)); - } - - // Int32Array - if (TypeKey.Int32Array in value) { - return Int32Array.from(mapValue(TypeKey.Int32Array, value)); - } - - // BigInt64Array - if (TypeKey.BigInt64Array in value) { - return BigInt64Array.from(mapValue(TypeKey.BigInt64Array, value)); - } - - // Uint8Array - if (TypeKey.Uint8Array in value) { - return Uint8Array.from(mapValue(TypeKey.Uint8Array, value)); - } - - // Uint16Array - if (TypeKey.Uint16Array in value) { - return Uint16Array.from(mapValue(TypeKey.Uint16Array, value)); - } - - // Uint32Array - if (TypeKey.Uint32Array in value) { - return Uint32Array.from(mapValue(TypeKey.Uint32Array, value)); - } - - // BigUint64Array - if (TypeKey.BigUint64Array in value) { - return BigUint64Array.from(mapValue(TypeKey.BigUint64Array, value)); + primary(indexKey); } - // Uint8ClampedArray - if (TypeKey.Uint8ClampedArray in value) { - return Uint8ClampedArray.from(mapValue(TypeKey.Uint8ClampedArray, value)); + if (!secondary || !id) { + return; } - // Float16Array - if (TypeKey.Float16Array in value) { - return Float16Array.from(mapValue(TypeKey.Float16Array, value)); - } - - // Float32Array - if (TypeKey.Float32Array in value) { - return Float32Array.from(mapValue(TypeKey.Float32Array, value)); - } - - // Float64Array - if (TypeKey.Float64Array in value) { - return Float64Array.from(mapValue(TypeKey.Float64Array, value)); - } - - // ArrayBuffer - if (TypeKey.ArrayBuffer in value) { - const uint8array = Uint8Array.from(mapValue(TypeKey.ArrayBuffer, value)); - return uint8array.buffer; - } - - // DataView - if (TypeKey.DataView in value) { - const uint8array = Uint8Array.from(mapValue(TypeKey.DataView, value)); - return new DataView(uint8array.buffer); - } - - // Set - if (TypeKey.Set in value) { - return new Set(mapValue>(TypeKey.Set, value)); - } - - // Map - if (TypeKey.Map in value) { - return new Map(mapValue>(TypeKey.Map, value)); - } + // Handle secondary indices + for (const index of collection._secondaryIndexList) { + const indexValue = data[index] as KvId | undefined; + if (typeof indexValue === "undefined") continue; - // Array - if (Array.isArray(value)) { - return value.map(_reviver); - } + const encoded = await encodeData(indexValue, collection._encoder); - // KvObject - if (isKvObject(value)) { - return Object.fromEntries( - Object.entries(value).map(([k, v]) => [k, _reviver(v)]), + const indexKey = extendKey( + collection._keys.secondaryIndex, + index, + encoded, + id, ); - } - - // Return value - return value; -} - -/** - * Additional revival steps to perform after initial parse. - * - * @param value - * @returns - */ -export function postReviver(value: T): T { - // Return value if not an object - if ( - value === undefined || - value === null || - typeof value !== "object" - ) { - return value; - } - // undefined - if (TypeKey.Undefined in value) { - return undefined as T; + secondary(indexKey); } - - // Array - if (Array.isArray(value)) { - return value.map(postReviver) as T; - } - - // Set - if (value instanceof Set) { - return new Set( - Array.from(value.values()).map(postReviver), - ) as T; - } - - // Map - if (value instanceof Map) { - return new Map( - Array.from(value.entries()).map(([k, v]) => [k, postReviver(v)]), - ) as T; - } - - // KvObject - if (isKvObject(value)) { - return Object.fromEntries( - Object.entries(value).map(([k, v]) => [k, postReviver(v)]), - ) as T; - } - - return value; -} - -/** - * Map from special type entry to value. - * - * @param key - Type key. - * @param value - JSON value to map from. - * @returns Mapped value. - */ -export function mapValue(key: string, value: unknown) { - return (value as Record)[key]; } diff --git a/tests/collection/add.test.ts b/tests/collection/add.test.ts index b58c416..67c8799 100644 --- a/tests/collection/add.test.ts +++ b/tests/collection/add.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1, mockUserInvalid } from "../mocks.ts"; import { useDb } from "../utils.ts"; diff --git a/tests/collection/addMany.test.ts b/tests/collection/addMany.test.ts index bcb664c..c5b257a 100644 --- a/tests/collection/addMany.test.ts +++ b/tests/collection/addMany.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { generateInvalidUsers, generateUsers, useDb } from "../utils.ts"; Deno.test("collection - addMany", async (t) => { diff --git a/tests/collection/count.test.ts b/tests/collection/count.test.ts index d900eec..e4d3daf 100644 --- a/tests/collection/count.test.ts +++ b/tests/collection/count.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { generateUsers } from "../utils.ts"; import { useDb } from "../utils.ts"; diff --git a/tests/collection/delete.test.ts b/tests/collection/delete.test.ts index 6a2e03a..44f1623 100644 --- a/tests/collection/delete.test.ts +++ b/tests/collection/delete.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1 } from "../mocks.ts"; import { generateUsers, useDb } from "../utils.ts"; diff --git a/tests/collection/deleteMany.test.ts b/tests/collection/deleteMany.test.ts index 15a0501..12809de 100644 --- a/tests/collection/deleteMany.test.ts +++ b/tests/collection/deleteMany.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { generateUsers, useDb } from "../utils.ts"; Deno.test("collection - deleteMany", async (t) => { diff --git a/tests/collection/enqueue.test.ts b/tests/collection/enqueue.test.ts index 1885919..2a3b668 100644 --- a/tests/collection/enqueue.test.ts +++ b/tests/collection/enqueue.test.ts @@ -6,7 +6,7 @@ import { type QueueMessage, } from "../../mod.ts"; import { createHandlerId } from "../../src/utils.ts"; -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { createResolver, useDb, useKv } from "../utils.ts"; Deno.test("collection - enqueue", async (t) => { @@ -16,8 +16,9 @@ Deno.test("collection - enqueue", async (t) => { const undeliveredId = "undelivered"; const sleeper = createResolver(); - const db = kvdex(kv, { - numbers: collection(model()), + const db = kvdex({ + kv, + schema: { numbers: collection(model()) }, }); const handlerId = createHandlerId(db.numbers._keys.base, undefined); diff --git a/tests/collection/find.test.ts b/tests/collection/find.test.ts index b45e4d0..674617b 100644 --- a/tests/collection/find.test.ts +++ b/tests/collection/find.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1 } from "../mocks.ts"; import { useDb } from "../utils.ts"; diff --git a/tests/collection/findMany.test.ts b/tests/collection/findMany.test.ts index dbc1e64..70efc42 100644 --- a/tests/collection/findMany.test.ts +++ b/tests/collection/findMany.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { generateUsers, useDb } from "../utils.ts"; Deno.test("collection - findMany", async (t) => { diff --git a/tests/collection/forEach.test.ts b/tests/collection/forEach.test.ts index 4f7c394..2c08169 100644 --- a/tests/collection/forEach.test.ts +++ b/tests/collection/forEach.test.ts @@ -1,5 +1,5 @@ import type { Document } from "../../mod.ts"; -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import type { User } from "../models.ts"; import { generateUsers } from "../utils.ts"; import { useDb } from "../utils.ts"; diff --git a/tests/collection/getMany.test.ts b/tests/collection/getMany.test.ts index 0808a31..8e9050b 100644 --- a/tests/collection/getMany.test.ts +++ b/tests/collection/getMany.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { generateUsers, useDb } from "../utils.ts"; Deno.test("collection - getMany", async (t) => { diff --git a/tests/collection/getOne.test.ts b/tests/collection/getOne.test.ts index e541f0c..25d7c73 100644 --- a/tests/collection/getOne.test.ts +++ b/tests/collection/getOne.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { sleep, useDb } from "../utils.ts"; import { mockUser1, mockUser2 } from "../mocks.ts"; diff --git a/tests/collection/history.test.ts b/tests/collection/history.test.ts index 7bac1e6..a2261f4 100644 --- a/tests/collection/history.test.ts +++ b/tests/collection/history.test.ts @@ -1,5 +1,5 @@ import { collection, kvdex, model } from "../../mod.ts"; -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { sleep, useKv } from "../utils.ts"; import { mockUser1, mockUser2, mockUser3 } from "../mocks.ts"; import type { User } from "../models.ts"; @@ -9,8 +9,9 @@ Deno.test("collection - history", async (t) => { "Should persist history of multiple inserts in correct order", async () => { await useKv(async (kv) => { - const db = kvdex(kv, { - users: collection(model(), { history: true }), + const db = kvdex({ + kv, + schema: { users: collection(model(), { history: true }) }, }); const id = "id"; @@ -37,8 +38,9 @@ Deno.test("collection - history", async (t) => { "Should persist history of multiple inserts in correct order after deleting", async () => { await useKv(async (kv) => { - const db = kvdex(kv, { - users: collection(model(), { history: true }), + const db = kvdex({ + kv, + schema: { users: collection(model(), { history: true }) }, }); const id = "id"; @@ -73,8 +75,9 @@ Deno.test("collection - history", async (t) => { "Should persist history of multiple inserts and updates in correct order", async () => { await useKv(async (kv) => { - const db = kvdex(kv, { - users: collection(model(), { history: true }), + const db = kvdex({ + kv, + schema: { users: collection(model(), { history: true }) }, }); const id = "id"; @@ -101,8 +104,9 @@ Deno.test("collection - history", async (t) => { "Should persist version history of insert and delete by deleteMany()", async () => { await useKv(async (kv) => { - const db = kvdex(kv, { - users: collection(model(), { history: true }), + const db = kvdex({ + kv, + schema: { users: collection(model(), { history: true }) }, }); const id = "id"; @@ -132,8 +136,9 @@ Deno.test("collection - history", async (t) => { "Should not find history", async () => { await useKv(async (kv) => { - const db = kvdex(kv, { - users: collection(model()), + const db = kvdex({ + kv, + schema: { users: collection(model()) }, }); const id = "id"; @@ -150,8 +155,9 @@ Deno.test("collection - history", async (t) => { await t.step("Should find filtered history", async () => { await useKv(async (kv) => { - const db = kvdex(kv, { - users: collection(model(), { history: true }), + const db = kvdex({ + kv, + schema: { users: collection(model(), { history: true }) }, }); const id = "id"; @@ -188,8 +194,9 @@ Deno.test("collection - history", async (t) => { await t.step("Should delete all document history", async () => { await useKv(async (kv) => { - const db = kvdex(kv, { - users: collection(model(), { history: true }), + const db = kvdex({ + kv, + schema: { users: collection(model(), { history: true }) }, }); const id = "id"; diff --git a/tests/collection/listenQueue.test.ts b/tests/collection/listenQueue.test.ts index 0532950..e33bd2b 100644 --- a/tests/collection/listenQueue.test.ts +++ b/tests/collection/listenQueue.test.ts @@ -10,7 +10,7 @@ import { UNDELIVERED_KEY_PREFIX, } from "../../src/constants.ts"; import { createHandlerId, extendKey } from "../../src/utils.ts"; -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { createResolver, sleep, useKv } from "../utils.ts"; Deno.test("collection - listenQueue", async (t) => { @@ -20,8 +20,11 @@ Deno.test("collection - listenQueue", async (t) => { const undeliveredId = "id"; const sleeper = createResolver(); - const db = kvdex(kv, { - numbers: collection(model()), + const db = kvdex({ + kv, + schema: { + numbers: collection(model()), + }, }); const handlerId = createHandlerId(db.numbers._keys.base, undefined); @@ -60,8 +63,9 @@ Deno.test("collection - listenQueue", async (t) => { await t.step("Should not receive db queue message", async () => { await useKv(async (kv) => { - const db = kvdex(kv, { - numbers: collection(model()), + const db = kvdex({ + kv, + schema: { numbers: collection(model()) }, }); let assertion = true; diff --git a/tests/collection/map.test.ts b/tests/collection/map.test.ts index 9ed432b..19649ec 100644 --- a/tests/collection/map.test.ts +++ b/tests/collection/map.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { generateUsers, useDb } from "../utils.ts"; Deno.test("collection - map", async (t) => { diff --git a/tests/collection/properties.test.ts b/tests/collection/properties.test.ts index e535a7a..a4680fa 100644 --- a/tests/collection/properties.test.ts +++ b/tests/collection/properties.test.ts @@ -1,7 +1,7 @@ import { collection, type Document, kvdex, model } from "../../mod.ts"; import { ID_KEY_PREFIX, KVDEX_KEY_PREFIX } from "../../src/constants.ts"; import { extendKey, keyEq } from "../../src/utils.ts"; -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1, mockUser2, mockUser3 } from "../mocks.ts"; import type { User } from "../models.ts"; import { generateUsers, useDb, useKv } from "../utils.ts"; @@ -21,16 +21,19 @@ Deno.test("collection - properties", async (t) => { await t.step("Should generate ids with custom id generator", async () => { await useKv((kv) => { - const db = kvdex(kv, { - users1: collection(model(), { - idGenerator: () => Math.random(), - }), - users2: collection(model(), { - idGenerator: (data) => data.username, - indices: { - username: "primary", - }, - }), + const db = kvdex({ + kv, + schema: { + users1: collection(model(), { + idGenerator: () => Math.random(), + }), + users2: collection(model(), { + idGenerator: (data) => data.username, + indices: { + username: "primary", + }, + }), + }, }); const id1 = db.users1._idGenerator(mockUser1); @@ -282,16 +285,19 @@ Deno.test("collection - properties", async (t) => { await t.step("Should successfully generate id asynchronously", async () => { await useKv(async (kv) => { - const db = kvdex(kv, { - test: collection(model(), { - idGenerator: async (user) => { - const buffer = await crypto.subtle.digest( - "SHA-256", - new ArrayBuffer(user.age), - ); - return Math.random() * buffer.byteLength; - }, - }), + const db = kvdex({ + kv, + schema: { + test: collection(model(), { + idGenerator: async (user) => { + const buffer = await crypto.subtle.digest( + "SHA-256", + new ArrayBuffer(user.age), + ); + return Math.random() * buffer.byteLength; + }, + }), + }, }); const cr1 = await db.test.add(mockUser1); diff --git a/tests/collection/set.test.ts b/tests/collection/set.test.ts index 6aa2baa..3dd64f7 100644 --- a/tests/collection/set.test.ts +++ b/tests/collection/set.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1, mockUser2, mockUserInvalid } from "../mocks.ts"; import { useDb } from "../utils.ts"; diff --git a/tests/collection/types.test.ts b/tests/collection/types.test.ts index 326f194..c2ae412 100644 --- a/tests/collection/types.test.ts +++ b/tests/collection/types.test.ts @@ -1,5 +1,5 @@ import { collection, kvdex, model } from "../../mod.ts"; -import { assert, assertEquals } from "../test.deps.ts"; +import { assert, assertEquals } from "@std/assert"; import { useKv } from "../utils.ts"; import { VALUES } from "../values.ts"; @@ -15,7 +15,7 @@ Deno.test("collection - types", async (t) => { ) => [i, collection(model())]), ); - const db = kvdex(kv, schema); + const db = kvdex({ kv, schema }); const crs = await Promise.all(VALUES.map((val, i) => db[i].add(val))); assert(crs.every((cr) => cr.ok)); diff --git a/tests/collection/update.test.ts b/tests/collection/update.test.ts index af59bca..c06633d 100644 --- a/tests/collection/update.test.ts +++ b/tests/collection/update.test.ts @@ -1,5 +1,5 @@ import { collection, kvdex, model } from "../../mod.ts"; -import { assert, assertEquals } from "../test.deps.ts"; +import { assert, assertEquals } from "@std/assert"; import { mockUser1, mockUser2, mockUserInvalid } from "../mocks.ts"; import { useDb, useKv } from "../utils.ts"; @@ -108,10 +108,13 @@ Deno.test("collection - update", async (t) => { "Should update documents of type Array, Set and Map using merge", async () => { await useKv(async (kv) => { - const db = kvdex(kv, { - arrays: collection(model()), - sets: collection(model>()), - maps: collection(model>()), + const db = kvdex({ + kv, + schema: { + arrays: collection(model()), + sets: collection(model>()), + maps: collection(model>()), + }, }); const val1 = [1, 2, 4]; @@ -168,10 +171,13 @@ Deno.test("collection - update", async (t) => { "Should update documents of primitive and built-in object types using replace", async () => { await useKv(async (kv) => { - const db = kvdex(kv, { - numbers: collection(model()), - strings: collection(model()), - dates: collection(model()), + const db = kvdex({ + kv, + schema: { + numbers: collection(model()), + strings: collection(model()), + dates: collection(model()), + }, }); const cr1 = await db.numbers.add(10); diff --git a/tests/collection/updateMany.test.ts b/tests/collection/updateMany.test.ts index f8a6d1f..fdf1f81 100644 --- a/tests/collection/updateMany.test.ts +++ b/tests/collection/updateMany.test.ts @@ -1,5 +1,5 @@ import { collection, kvdex, model } from "../../mod.ts"; -import { assert, assertEquals } from "../test.deps.ts"; +import { assert, assertEquals } from "@std/assert"; import { mockUser1, mockUserInvalid } from "../mocks.ts"; import { generateNumbers, generateUsers, useDb, useKv } from "../utils.ts"; @@ -123,10 +123,13 @@ Deno.test("collection - updateMany", async (t) => { "Should update 1000 documents of type Array, Set and Map using merge", async () => { await useKv(async (kv) => { - const db = kvdex(kv, { - arrays: collection(model()), - sets: collection(model>()), - maps: collection(model>()), + const db = kvdex({ + kv, + schema: { + arrays: collection(model()), + sets: collection(model>()), + maps: collection(model>()), + }, }); const val1 = [1, 2, 4]; @@ -197,10 +200,13 @@ Deno.test("collection - updateMany", async (t) => { "Should update 1000 documents of types primitive and built-in object using replace", async () => { await useKv(async (kv) => { - const db = kvdex(kv, { - numbers: collection(model()), - strings: collection(model()), - dates: collection(model()), + const db = kvdex({ + kv, + schema: { + numbers: collection(model()), + strings: collection(model()), + dates: collection(model()), + }, }); const numbers = generateNumbers(1_000); diff --git a/tests/collection/updateOne.test.ts b/tests/collection/updateOne.test.ts index 137b3be..008a306 100644 --- a/tests/collection/updateOne.test.ts +++ b/tests/collection/updateOne.test.ts @@ -1,5 +1,5 @@ import { collection, kvdex, model } from "../../mod.ts"; -import { assert, assertEquals, assertNotEquals } from "../test.deps.ts"; +import { assert, assertEquals, assertNotEquals } from "@std/assert"; import { mockUser1, mockUser2, mockUser3, mockUserInvalid } from "../mocks.ts"; import { generateNumbers, @@ -144,10 +144,13 @@ Deno.test("collection - updateOne", async (t) => { "Should update only one document of type Array, Set and Map using merge", async () => { await useKv(async (kv) => { - const db = kvdex(kv, { - arrays: collection(model()), - sets: collection(model>()), - maps: collection(model>()), + const db = kvdex({ + kv, + schema: { + arrays: collection(model()), + sets: collection(model>()), + maps: collection(model>()), + }, }); const val1 = [1, 2, 4]; @@ -218,10 +221,13 @@ Deno.test("collection - updateOne", async (t) => { "Should update only one document of types primitive and built-in object using replace", async () => { await useKv(async (kv) => { - const db = kvdex(kv, { - numbers: collection(model()), - strings: collection(model()), - dates: collection(model()), + const db = kvdex({ + kv, + schema: { + numbers: collection(model()), + strings: collection(model()), + dates: collection(model()), + }, }); const numbers = generateNumbers(1_000); diff --git a/tests/collection/upsert.test.ts b/tests/collection/upsert.test.ts index 3e23ae1..f2c3225 100644 --- a/tests/collection/upsert.test.ts +++ b/tests/collection/upsert.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1, mockUser2, mockUser3 } from "../mocks.ts"; import type { User } from "../models.ts"; import { useDb } from "../utils.ts"; diff --git a/tests/collection/watch.test.ts b/tests/collection/watch.test.ts index 88b4dd9..9867291 100644 --- a/tests/collection/watch.test.ts +++ b/tests/collection/watch.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1, mockUser2, mockUser3 } from "../mocks.ts"; import { sleep, useDb } from "../utils.ts"; import type { Document } from "../../mod.ts"; diff --git a/tests/collection/watchMany.test.ts b/tests/collection/watchMany.test.ts index 0c589a6..296709c 100644 --- a/tests/collection/watchMany.test.ts +++ b/tests/collection/watchMany.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1, mockUser2, mockUser3 } from "../mocks.ts"; import { sleep, useDb } from "../utils.ts"; import type { Document } from "../../mod.ts"; diff --git a/tests/db/atomic.test.ts b/tests/db/atomic.test.ts index afd372b..7e8c4a0 100644 --- a/tests/db/atomic.test.ts +++ b/tests/db/atomic.test.ts @@ -6,7 +6,7 @@ import { type QueueMessage, } from "../../mod.ts"; import { createHandlerId } from "../../src/utils.ts"; -import { assert } from "../test.deps.ts"; +import { assert, assertEquals, assertThrows } from "@std/assert"; import { mockUser1, mockUser2, mockUserInvalid } from "../mocks.ts"; import { sleep, useDb, useKv } from "../utils.ts"; @@ -46,6 +46,42 @@ Deno.test("db - atomic", async (t) => { }, ); + await t.step("Should overwrite document in standard collection", async () => { + await useDb(async (db) => { + const cr1 = await db.users.add(mockUser1); + assert(cr1.ok); + + const cr2 = await db + .atomic((schema) => schema.users) + .set(cr1.id, mockUser2, { overwrite: true }) + .commit(); + + assert(cr2.ok); + + const count = await db.users.count(); + assert(count === 1); + + const doc = await db.users.find(cr1.id); + assertEquals(doc?.value, mockUser2); + }); + }); + + await t.step( + "Should throw when trying to overwrite document in indexable collection", + async () => { + await useDb(async (db) => { + const cr1 = await db.i_users.add(mockUser1); + assert(cr1.ok); + + assertThrows(() => { + db + .atomic((schema) => schema.i_users) + .set(cr1.id, mockUser2, { overwrite: true } as any); + }); + }); + }, + ); + await t.step("Should delete document", async () => { await useDb(async (db) => { const cr1 = await db.users.add(mockUser1); @@ -272,8 +308,9 @@ Deno.test("db - atomic", async (t) => { const data = "data"; const undeliveredId = "undelivered"; - const db = kvdex(kv, { - numbers: collection(model()), + const db = kvdex({ + kv, + schema: { numbers: collection(model()) }, }); const handlerId = createHandlerId(db.numbers._keys.base, undefined); @@ -373,8 +410,9 @@ Deno.test("db - atomic", async (t) => { await t.step("Should retain history in correct order", async () => { await useKv(async (kv) => { - const db = kvdex(kv, { - numbers: collection(model(), { history: true }), + const db = kvdex({ + kv, + schema: { numbers: collection(model(), { history: true }) }, }); const id = "id"; diff --git a/tests/db/countAll.test.ts b/tests/db/countAll.test.ts index b0f30ff..0749254 100644 --- a/tests/db/countAll.test.ts +++ b/tests/db/countAll.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { generateLargeUsers, generateUsers, useDb } from "../utils.ts"; Deno.test("db - countAll", async (t) => { diff --git a/tests/db/deleteAll.test.ts b/tests/db/deleteAll.test.ts index f4fe71d..350d017 100644 --- a/tests/db/deleteAll.test.ts +++ b/tests/db/deleteAll.test.ts @@ -1,28 +1,32 @@ import { collection, kvdex, model } from "../../mod.ts"; -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import type { User } from "../models.ts"; import { generateLargeUsers, generateUsers, useKv } from "../utils.ts"; +import { jsonEncoder } from "../../src/ext/encoding/mod.ts"; Deno.test("db - deleteAll", async (t) => { await t.step( "Should delete all documents from the database without deleting history entries", async () => { await useKv(async (kv) => { - const db = kvdex(kv, { - i_users: collection(model(), { - history: true, - indices: { - username: "primary", - age: "secondary", - }, - }), - s_users: collection(model(), { - serialize: "json", - history: true, - }), - u64s: collection(model(), { - history: true, - }), + const db = kvdex({ + kv, + schema: { + i_users: collection(model(), { + history: true, + indices: { + username: "primary", + age: "secondary", + }, + }), + s_users: collection(model(), { + encoder: jsonEncoder(), + history: true, + }), + u64s: collection(model(), { + history: true, + }), + }, }); const users = generateUsers(100); diff --git a/tests/db/enqueue.test.ts b/tests/db/enqueue.test.ts index b76cc53..3f69997 100644 --- a/tests/db/enqueue.test.ts +++ b/tests/db/enqueue.test.ts @@ -7,7 +7,7 @@ import { } from "../../mod.ts"; import { KVDEX_KEY_PREFIX } from "../../src/constants.ts"; import { createHandlerId } from "../../src/utils.ts"; -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { createResolver, useKv } from "../utils.ts"; Deno.test("db - enqueue", async (t) => { @@ -17,8 +17,9 @@ Deno.test("db - enqueue", async (t) => { const undeliveredId = "undelivered"; const sleeper = createResolver(); - const db = kvdex(kv, { - numbers: collection(model()), + const db = kvdex({ + kv, + schema: { numbers: collection(model()) }, }); const handlerId = createHandlerId([KVDEX_KEY_PREFIX], undefined); @@ -51,8 +52,9 @@ Deno.test("db - enqueue", async (t) => { const topic = "topic"; const sleeper = createResolver(); - const db = kvdex(kv, { - numbers: collection(model()), + const db = kvdex({ + kv, + schema: { numbers: collection(model()) }, }); let assertion1 = false; @@ -88,8 +90,9 @@ Deno.test("db - enqueue", async (t) => { const undeliveredId = "undelivered"; const sleeper = createResolver(); - const db = kvdex(kv, { - numbers: collection(model()), + const db = kvdex({ + kv, + schema: { numbers: collection(model()) }, }); const handlerId = createHandlerId([KVDEX_KEY_PREFIX], undefined); diff --git a/tests/db/indexable_atomic.test.ts b/tests/db/indexable_atomic.test.ts index ccdf37f..cda81fb 100644 --- a/tests/db/indexable_atomic.test.ts +++ b/tests/db/indexable_atomic.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1 } from "../mocks.ts"; import { useDb } from "../utils.ts"; diff --git a/tests/db/kvdex.test.ts b/tests/db/kvdex.test.ts index 38906c4..5c9f347 100644 --- a/tests/db/kvdex.test.ts +++ b/tests/db/kvdex.test.ts @@ -1,6 +1,6 @@ import { collection, kvdex, model } from "../../mod.ts"; import { KVDEX_KEY_PREFIX } from "../../src/constants.ts"; -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { useKv } from "../utils.ts"; Deno.test("db - kvdex", async (t) => { @@ -8,10 +8,13 @@ Deno.test("db - kvdex", async (t) => { "Should create unique keys for collections with equal name in different nestings", async () => { await useKv((kv) => { - const db = kvdex(kv, { - numbers: collection(model()), - nested: { + const db = kvdex({ + kv, + schema: { numbers: collection(model()), + nested: { + numbers: collection(model()), + }, }, }); diff --git a/tests/db/listenQueue.test.ts b/tests/db/listenQueue.test.ts index 8fccb28..c0e3d3c 100644 --- a/tests/db/listenQueue.test.ts +++ b/tests/db/listenQueue.test.ts @@ -7,14 +7,14 @@ import { } from "../../mod.ts"; import { KVDEX_KEY_PREFIX } from "../../src/constants.ts"; import { createHandlerId } from "../../src/utils.ts"; -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { createResolver, sleep, useKv } from "../utils.ts"; Deno.test("db - listenQueue", async (t) => { await t.step("Should receive message with string data", async () => { await useKv(async (kv) => { const data = "data"; - const db = kvdex(kv, {}); + const db = kvdex({ kv }); const sleeper = createResolver(); const handlerId = createHandlerId([KVDEX_KEY_PREFIX], undefined); @@ -45,8 +45,9 @@ Deno.test("db - listenQueue", async (t) => { await useKv(async (kv) => { const data = "data"; - const db = kvdex(kv, { - numbers: collection(model()), + const db = kvdex({ + kv, + schema: { numbers: collection(model()) }, }); let assertion = true; diff --git a/tests/db/loop.test.ts b/tests/db/loop.test.ts index 20550cf..a35f85e 100644 --- a/tests/db/loop.test.ts +++ b/tests/db/loop.test.ts @@ -1,5 +1,5 @@ import { kvdex } from "../../mod.ts"; -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { createResolver, useDb, useKv } from "../utils.ts"; Deno.test("db - loop", async (t) => { @@ -37,7 +37,7 @@ Deno.test("db - loop", async (t) => { "Should run loop for 10 iterations and carry accumulated result", async () => { await useKv(async (kv) => { - const db = kvdex(kv, {}); + const db = kvdex({ kv }); const { resolve, promise } = createResolver(); let count = 0; diff --git a/tests/db/properties.test.ts b/tests/db/properties.test.ts index d6a8d34..fe0994e 100644 --- a/tests/db/properties.test.ts +++ b/tests/db/properties.test.ts @@ -4,13 +4,13 @@ import { kvdex } from "../../mod.ts"; Deno.test("db - properties", async (t) => { await t.step("Should allow native Deno KV type", async () => { const kv = await Deno.openKv(); - kvdex(kv, {}); + kvdex({ kv }); kv.close(); }); await t.step("Should allow NPM Deno KV type", async () => { const kv = await openKv(); - kvdex(kv, {}); + kvdex({ kv }); kv.close(); }); }); diff --git a/tests/db/setInterval.test.ts b/tests/db/setInterval.test.ts index 49a6c7a..94c8cdd 100644 --- a/tests/db/setInterval.test.ts +++ b/tests/db/setInterval.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { createResolver, useDb } from "../utils.ts"; Deno.test("db - setInterval", async (t) => { diff --git a/tests/db/wipe.test.ts b/tests/db/wipe.test.ts index 7bb84c0..6e28199 100644 --- a/tests/db/wipe.test.ts +++ b/tests/db/wipe.test.ts @@ -1,28 +1,32 @@ import { collection, kvdex, model } from "../../mod.ts"; -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import type { User } from "../models.ts"; import { generateLargeUsers, generateUsers, useKv } from "../utils.ts"; +import { jsonEncoder } from "../../src/ext/encoding/mod.ts"; Deno.test("db - wipe", async (t) => { await t.step( "Should delete all kvdex entries from the database, including history entries", async () => { await useKv(async (kv) => { - const db = kvdex(kv, { - i_users: collection(model(), { - history: true, - indices: { - username: "primary", - age: "secondary", - }, - }), - s_users: collection(model(), { - serialize: "json", - history: true, - }), - u64s: collection(model(), { - history: true, - }), + const db = kvdex({ + kv, + schema: { + i_users: collection(model(), { + history: true, + indices: { + username: "primary", + age: "secondary", + }, + }), + s_users: collection(model(), { + encoder: jsonEncoder(), + history: true, + }), + u64s: collection(model(), { + history: true, + }), + }, }); const users = generateUsers(100); diff --git a/tests/document/flat.test.ts b/tests/document/flat.test.ts index 12bccfb..61173d6 100644 --- a/tests/document/flat.test.ts +++ b/tests/document/flat.test.ts @@ -1,5 +1,5 @@ import { Document, model } from "../../mod.ts"; -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1 } from "../mocks.ts"; import type { User } from "../models.ts"; diff --git a/tests/document/properties.test.ts b/tests/document/properties.test.ts index 75d035b..dd496f7 100644 --- a/tests/document/properties.test.ts +++ b/tests/document/properties.test.ts @@ -1,5 +1,6 @@ import { Document, model } from "../../mod.ts"; -import { assert, z } from "../test.deps.ts"; +import { assert } from "@std/assert"; +import { z } from "zod"; Deno.test("document - properties", async (t) => { await t.step("Should create document with Model", () => { diff --git a/tests/ext/encoder.test.ts b/tests/ext/encoder.test.ts new file mode 100644 index 0000000..1cd3bb7 --- /dev/null +++ b/tests/ext/encoder.test.ts @@ -0,0 +1,64 @@ +import { assert, assertEquals } from "@std/assert"; +import { VALUES } from "../values.ts"; +import { + jsonDeserialize, + jsonSerialize, + v8Deserialize, + v8Serialize, +} from "../../src/ext/encoding/mod.ts"; + +Deno.test("ext - encoder", async (t) => { + await t.step("json", async (t) => { + await t.step( + "Should successfully serialize all KvValue type values", + () => { + const serialized = VALUES.map(jsonSerialize); + assert(serialized.every((val) => val instanceof Uint8Array)); + }, + ); + + await t.step( + "Should successfully deserialize all KvValue type values from Uint8Array", + () => { + VALUES.forEach((val) => { + const seralized = jsonSerialize(val); + const deserialized = jsonDeserialize(seralized); + assertEquals(val, deserialized); + }); + }, + ); + }); + + await t.step("v8", async (t) => { + await t.step( + "Should successfully serialize all KvValue type values", + () => { + const serialized = VALUES.map((val) => { + try { + return v8Serialize(val); + } catch (e) { + throw new Error( + `Failed to serialize value: ${val}, Error: ${e}`, + ); + } + }); + assert(serialized.every((val) => val instanceof Uint8Array)); + }, + ); + + await t.step( + "Should successfully deserialize all KvValue type values from Uint8Array", + () => { + VALUES.forEach((val) => { + const serialized = v8Serialize(val); + try { + const deserialized = v8Deserialize(serialized); + assertEquals(val, deserialized); + } catch (e) { + throw new Error(`Failed to deserialize value: ${val}, Error: ${e}`); + } + }); + }, + ); + }); +}); diff --git a/tests/ext/kv.test.ts b/tests/ext/kv.test.ts index 9c57ded..5d8e34f 100644 --- a/tests/ext/kv.test.ts +++ b/tests/ext/kv.test.ts @@ -1,6 +1,6 @@ import { MapKv } from "../../src/ext/kv/map_kv.ts"; import { StorageAdapter } from "../../src/ext/kv/mod.ts"; -import { assert, assertEquals } from "../test.deps.ts"; +import { assert, assertEquals } from "@std/assert"; import { sleep } from "../utils.ts"; async function useStore(fn: (store: StorageAdapter) => unknown) { diff --git a/tests/ext/migrate.test.ts b/tests/ext/migrate.test.ts index e6b99d6..3829bb9 100644 --- a/tests/ext/migrate.test.ts +++ b/tests/ext/migrate.test.ts @@ -2,7 +2,7 @@ import { migrate } from "../../src/ext/migrate/mod.ts"; import { collection } from "../../src/collection.ts"; import { kvdex } from "../../src/kvdex.ts"; import { model } from "../../src/model.ts"; -import { assert, assertEquals } from "../test.deps.ts"; +import { assert, assertEquals } from "@std/assert"; import { TArray, TBigint, @@ -34,74 +34,78 @@ import { TUint8ClampedArray, TUndefined, } from "../values.ts"; +import { jsonEncoder } from "../../src/ext/encoding/mod.ts"; function createDb(kv: Deno.Kv) { - return kvdex(kv, { - c_TUndefined: collection(model()), - c_TNull: collection(model()), - c_TNaN: collection(model()), - c_TInfinity: collection(model()), - c_TNumber: collection(model()), - c_TString: collection(model()), - c_TBigint: collection(model()), - c_TKvU64: collection(model()), - c_TBoolean: collection(model()), - c_TInt8Array: collection(model()), - c_TInt16Array: collection(model()), - c_TInt32Array: collection(model()), - c_TBigInt64Array: collection(model()), - c_TUint8Array: collection(model()), - c_TUint16Array: collection(model()), - c_TUint32Array: collection(model()), - c_TBigUint64Array: collection(model()), - c_TUint8ClampedArray: collection(model()), - c_TFloat32Array: collection(model()), - c_TFloat64Array: collection(model()), - c_TBuffer: collection(model()), - c_TDataView: collection(model()), - c_TDate: collection(model()), - c_TError: collection(model()), - c_TRegExp: collection(model()), - c_TArray: collection(model()), - c_TObject: collection(model()), - c_TSet: collection(model()), - c_TMap: collection(model()), - s_TUndefined: collection(model()), - s_TNull: collection(model()), - s_TNaN: collection(model()), - s_TInfinity: collection(model()), - s_TNumber: collection(model()), - s_TString: collection(model()), - s_TBigint: collection(model()), - s_TKvU64: collection(model()), - s_TBoolean: collection(model()), - s_TInt8Array: collection(model()), - s_TInt16Array: collection(model()), - s_TInt32Array: collection(model()), - s_TBigInt64Array: collection(model()), - s_TUint8Array: collection(model()), - s_TUint16Array: collection(model()), - s_TUint32Array: collection(model()), - s_TBigUint64Array: collection(model()), - s_TUint8ClampedArray: collection(model()), - s_TFloat32Array: collection(model()), - s_TFloat64Array: collection(model()), - s_TBuffer: collection(model()), - s_TDataView: collection(model()), - s_TDate: collection(model()), - s_TError: collection(model()), - s_TRegExp: collection(model()), - s_TArray: collection(model()), - s_TObject: collection(model()), - s_TSet: collection(model()), - s_TMap: collection(model()), - i: collection(model(), { - indices: { TString: "primary", TNumber: "secondary" }, - }), - is: collection(model(), { - serialize: "json", - indices: { TString: "primary", TNumber: "secondary" }, - }), + return kvdex({ + kv, + schema: { + c_TUndefined: collection(model()), + c_TNull: collection(model()), + c_TNaN: collection(model()), + c_TInfinity: collection(model()), + c_TNumber: collection(model()), + c_TString: collection(model()), + c_TBigint: collection(model()), + c_TKvU64: collection(model()), + c_TBoolean: collection(model()), + c_TInt8Array: collection(model()), + c_TInt16Array: collection(model()), + c_TInt32Array: collection(model()), + c_TBigInt64Array: collection(model()), + c_TUint8Array: collection(model()), + c_TUint16Array: collection(model()), + c_TUint32Array: collection(model()), + c_TBigUint64Array: collection(model()), + c_TUint8ClampedArray: collection(model()), + c_TFloat32Array: collection(model()), + c_TFloat64Array: collection(model()), + c_TBuffer: collection(model()), + c_TDataView: collection(model()), + c_TDate: collection(model()), + c_TError: collection(model()), + c_TRegExp: collection(model()), + c_TArray: collection(model()), + c_TObject: collection(model()), + c_TSet: collection(model()), + c_TMap: collection(model()), + s_TUndefined: collection(model()), + s_TNull: collection(model()), + s_TNaN: collection(model()), + s_TInfinity: collection(model()), + s_TNumber: collection(model()), + s_TString: collection(model()), + s_TBigint: collection(model()), + s_TKvU64: collection(model()), + s_TBoolean: collection(model()), + s_TInt8Array: collection(model()), + s_TInt16Array: collection(model()), + s_TInt32Array: collection(model()), + s_TBigInt64Array: collection(model()), + s_TUint8Array: collection(model()), + s_TUint16Array: collection(model()), + s_TUint32Array: collection(model()), + s_TBigUint64Array: collection(model()), + s_TUint8ClampedArray: collection(model()), + s_TFloat32Array: collection(model()), + s_TFloat64Array: collection(model()), + s_TBuffer: collection(model()), + s_TDataView: collection(model()), + s_TDate: collection(model()), + s_TError: collection(model()), + s_TRegExp: collection(model()), + s_TArray: collection(model()), + s_TObject: collection(model()), + s_TSet: collection(model()), + s_TMap: collection(model()), + i: collection(model(), { + indices: { TString: "primary", TNumber: "secondary" }, + }), + is: collection(model(), { + encoder: jsonEncoder(), + indices: { TString: "primary", TNumber: "secondary" }, + }), + }, }); } diff --git a/tests/ext/zod.test.ts b/tests/ext/zod.test.ts index f133691..3d5ef62 100644 --- a/tests/ext/zod.test.ts +++ b/tests/ext/zod.test.ts @@ -1,4 +1,5 @@ -import { assert, z } from "../test.deps.ts"; +import { z } from "zod"; +import { assert } from "@std/assert"; import { KvArraySchema, KvIdSchema, @@ -6,7 +7,7 @@ import { KvValueSchema, } from "../../src/ext/zod/mod.ts"; import { collection, kvdex } from "../../mod.ts"; -import { useKv } from "../utils.ts"; +import { testEncoder, useKv } from "../utils.ts"; import { VALUES } from "../values.ts"; const UserSchema = z.object({ @@ -42,15 +43,18 @@ const kvArray = ["test", 10, true, 10n]; Deno.test("ext - zod", async (t) => { await t.step("Should correctly parse insert model", async () => { await useKv(async (kv) => { - const db = kvdex(kv, { - users: collection(UserSchema), - i_users: collection(UserSchema, { - indices: { - username: "primary", - age: "secondary", - }, - }), - s_users: collection(UserSchema, { serialize: "json" }), + const db = kvdex({ + kv, + schema: { + users: collection(UserSchema), + i_users: collection(UserSchema, { + indices: { + username: "primary", + age: "secondary", + }, + }), + s_users: collection(UserSchema, { encoder: testEncoder }), + }, }); const cr1 = await db.users.add({ @@ -87,15 +91,18 @@ Deno.test("ext - zod", async (t) => { "Should use base model when typing selected documents", async () => { await useKv(async (kv) => { - const db = kvdex(kv, { - users: collection(UserSchema), - i_users: collection(UserSchema, { - indices: { - username: "primary", - age: "secondary", - }, - }), - l_users: collection(UserSchema), + const db = kvdex({ + kv, + schema: { + users: collection(UserSchema), + i_users: collection(UserSchema, { + indices: { + username: "primary", + age: "secondary", + }, + }), + l_users: collection(UserSchema), + }, }); // Default values should not be inferred as optional when selecting diff --git a/tests/indexable_collection/add.test.ts b/tests/indexable_collection/add.test.ts index 4787674..77f94d9 100644 --- a/tests/indexable_collection/add.test.ts +++ b/tests/indexable_collection/add.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1, mockUserInvalid } from "../mocks.ts"; import { useDb } from "../utils.ts"; diff --git a/tests/indexable_collection/addMany.test.ts b/tests/indexable_collection/addMany.test.ts index c1c55cb..5243a2c 100644 --- a/tests/indexable_collection/addMany.test.ts +++ b/tests/indexable_collection/addMany.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1 } from "../mocks.ts"; import { generateInvalidUsers, generateUsers, useDb } from "../utils.ts"; diff --git a/tests/indexable_collection/count.test.ts b/tests/indexable_collection/count.test.ts index 6720ff5..13324a7 100644 --- a/tests/indexable_collection/count.test.ts +++ b/tests/indexable_collection/count.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { generateUsers, useDb } from "../utils.ts"; Deno.test("indexable_collection - count", async (t) => { diff --git a/tests/indexable_collection/countBySecondaryIndex.test.ts b/tests/indexable_collection/countBySecondaryIndex.test.ts index b89e334..1ca47bd 100644 --- a/tests/indexable_collection/countBySecondaryIndex.test.ts +++ b/tests/indexable_collection/countBySecondaryIndex.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1, mockUser2, mockUser3 } from "../mocks.ts"; import { useDb } from "../utils.ts"; diff --git a/tests/indexable_collection/countBySecondaryOrder.test.ts b/tests/indexable_collection/countBySecondaryOrder.test.ts index 956916e..9a8d204 100644 --- a/tests/indexable_collection/countBySecondaryOrder.test.ts +++ b/tests/indexable_collection/countBySecondaryOrder.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1, mockUser2, mockUsersWithAlteredAge } from "../mocks.ts"; import { useDb } from "../utils.ts"; diff --git a/tests/indexable_collection/delete.test.ts b/tests/indexable_collection/delete.test.ts index 779a464..fc5a511 100644 --- a/tests/indexable_collection/delete.test.ts +++ b/tests/indexable_collection/delete.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1 } from "../mocks.ts"; import { generateUsers, useDb } from "../utils.ts"; diff --git a/tests/indexable_collection/deleteByPrimaryIndex.test.ts b/tests/indexable_collection/deleteByPrimaryIndex.test.ts index dbcdcfe..c2ad634 100644 --- a/tests/indexable_collection/deleteByPrimaryIndex.test.ts +++ b/tests/indexable_collection/deleteByPrimaryIndex.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1 } from "../mocks.ts"; import { useDb } from "../utils.ts"; diff --git a/tests/indexable_collection/deleteBySecondaryIndex.test.ts b/tests/indexable_collection/deleteBySecondaryIndex.test.ts index 29df499..dea6a13 100644 --- a/tests/indexable_collection/deleteBySecondaryIndex.test.ts +++ b/tests/indexable_collection/deleteBySecondaryIndex.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1, mockUser2 } from "../mocks.ts"; import { useDb } from "../utils.ts"; diff --git a/tests/indexable_collection/deleteMany.test.ts b/tests/indexable_collection/deleteMany.test.ts index cc560d9..b91c93e 100644 --- a/tests/indexable_collection/deleteMany.test.ts +++ b/tests/indexable_collection/deleteMany.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { generateUsers, useDb } from "../utils.ts"; Deno.test("indexable_collection - deleteMany", async (t) => { diff --git a/tests/indexable_collection/deleteManyBySecondaryOrder.test.ts b/tests/indexable_collection/deleteManyBySecondaryOrder.test.ts index 3ee058b..a341213 100644 --- a/tests/indexable_collection/deleteManyBySecondaryOrder.test.ts +++ b/tests/indexable_collection/deleteManyBySecondaryOrder.test.ts @@ -1,4 +1,4 @@ -import { assert, assertEquals } from "../test.deps.ts"; +import { assert, assertEquals } from "@std/assert"; import { mockUser2, mockUsersWithAlteredAge } from "../mocks.ts"; import { useDb } from "../utils.ts"; diff --git a/tests/indexable_collection/enqueue.test.ts b/tests/indexable_collection/enqueue.test.ts index 92b0a5b..49895e3 100644 --- a/tests/indexable_collection/enqueue.test.ts +++ b/tests/indexable_collection/enqueue.test.ts @@ -6,7 +6,7 @@ import { type QueueMessage, } from "../../mod.ts"; import { createHandlerId } from "../../src/utils.ts"; -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import type { User } from "../models.ts"; import { createResolver, useDb, useKv } from "../utils.ts"; @@ -16,8 +16,9 @@ Deno.test("indexable_collection - enqueue", async (t) => { const data = "data"; const undeliveredId = "undelivered"; - const db = kvdex(kv, { - i_users: collection(model(), { indices: {} }), + const db = kvdex({ + kv, + schema: { i_users: collection(model(), { indices: {} }) }, }); const sleeper = createResolver(); diff --git a/tests/indexable_collection/find.test.ts b/tests/indexable_collection/find.test.ts index b7bc916..e6217f4 100644 --- a/tests/indexable_collection/find.test.ts +++ b/tests/indexable_collection/find.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1 } from "../mocks.ts"; import { useDb } from "../utils.ts"; diff --git a/tests/indexable_collection/findByPrimaryIndex.test.ts b/tests/indexable_collection/findByPrimaryIndex.test.ts index 82c0146..36a99f3 100644 --- a/tests/indexable_collection/findByPrimaryIndex.test.ts +++ b/tests/indexable_collection/findByPrimaryIndex.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1 } from "../mocks.ts"; import { TransformUserModel } from "../models.ts"; import { useDb } from "../utils.ts"; diff --git a/tests/indexable_collection/findBySecondaryIndex.test.ts b/tests/indexable_collection/findBySecondaryIndex.test.ts index e6708ef..faaa464 100644 --- a/tests/indexable_collection/findBySecondaryIndex.test.ts +++ b/tests/indexable_collection/findBySecondaryIndex.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1, mockUser2 } from "../mocks.ts"; import { TransformUserModel } from "../models.ts"; import { useDb } from "../utils.ts"; diff --git a/tests/indexable_collection/findMany.test.ts b/tests/indexable_collection/findMany.test.ts index b536cb4..c748964 100644 --- a/tests/indexable_collection/findMany.test.ts +++ b/tests/indexable_collection/findMany.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { generateUsers, useDb } from "../utils.ts"; Deno.test("indexable_collection - findMany", async (t) => { diff --git a/tests/indexable_collection/forEach.test.ts b/tests/indexable_collection/forEach.test.ts index 8da46d3..62169b1 100644 --- a/tests/indexable_collection/forEach.test.ts +++ b/tests/indexable_collection/forEach.test.ts @@ -1,5 +1,5 @@ import type { Document } from "../../mod.ts"; -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import type { User } from "../models.ts"; import { generateUsers, useDb } from "../utils.ts"; diff --git a/tests/indexable_collection/forEachBySecondaryIndex.test.ts b/tests/indexable_collection/forEachBySecondaryIndex.test.ts index 07ed07d..8a195e7 100644 --- a/tests/indexable_collection/forEachBySecondaryIndex.test.ts +++ b/tests/indexable_collection/forEachBySecondaryIndex.test.ts @@ -1,5 +1,5 @@ import type { Document } from "../../mod.ts"; -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1, mockUser2, mockUser3 } from "../mocks.ts"; import type { User } from "../models.ts"; import { useDb } from "../utils.ts"; diff --git a/tests/indexable_collection/forEachBySecondaryOrder.test.ts b/tests/indexable_collection/forEachBySecondaryOrder.test.ts index 5b43cca..17c65f3 100644 --- a/tests/indexable_collection/forEachBySecondaryOrder.test.ts +++ b/tests/indexable_collection/forEachBySecondaryOrder.test.ts @@ -1,5 +1,5 @@ import type { Document } from "../../mod.ts"; -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1, mockUser2, diff --git a/tests/indexable_collection/getMany.test.ts b/tests/indexable_collection/getMany.test.ts index 0235379..e7f4b26 100644 --- a/tests/indexable_collection/getMany.test.ts +++ b/tests/indexable_collection/getMany.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { generateUsers } from "../utils.ts"; import { useDb } from "../utils.ts"; diff --git a/tests/indexable_collection/getManyBySecondaryOrder.test.ts b/tests/indexable_collection/getManyBySecondaryOrder.test.ts index e9963d5..b445ed1 100644 --- a/tests/indexable_collection/getManyBySecondaryOrder.test.ts +++ b/tests/indexable_collection/getManyBySecondaryOrder.test.ts @@ -4,7 +4,7 @@ import { mockUser3, mockUsersWithAlteredAge, } from "../mocks.ts"; -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { useDb } from "../utils.ts"; Deno.test("indexable_collection - getManyBySecondaryOrder", async (t) => { diff --git a/tests/indexable_collection/getOne.test.ts b/tests/indexable_collection/getOne.test.ts index 69dfc50..b3f148f 100644 --- a/tests/indexable_collection/getOne.test.ts +++ b/tests/indexable_collection/getOne.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { sleep, useDb } from "../utils.ts"; import { mockUser1, mockUser2 } from "../mocks.ts"; diff --git a/tests/indexable_collection/getOneBySecondaryIndex.test.ts b/tests/indexable_collection/getOneBySecondaryIndex.test.ts index 113e63d..fb74645 100644 --- a/tests/indexable_collection/getOneBySecondaryIndex.test.ts +++ b/tests/indexable_collection/getOneBySecondaryIndex.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { sleep, useDb } from "../utils.ts"; import { mockUser1, mockUser2 } from "../mocks.ts"; diff --git a/tests/indexable_collection/getOneBySecondaryOrder.test.ts b/tests/indexable_collection/getOneBySecondaryOrder.test.ts index 4802cdb..91f2b78 100644 --- a/tests/indexable_collection/getOneBySecondaryOrder.test.ts +++ b/tests/indexable_collection/getOneBySecondaryOrder.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { useDb } from "../utils.ts"; import { mockUser3, mockUsersWithAlteredAge } from "../mocks.ts"; diff --git a/tests/indexable_collection/history.test.ts b/tests/indexable_collection/history.test.ts index f7fcf65..8f11f7f 100644 --- a/tests/indexable_collection/history.test.ts +++ b/tests/indexable_collection/history.test.ts @@ -1,5 +1,5 @@ import { collection, kvdex, model } from "../../mod.ts"; -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { generateUsers, sleep, useKv } from "../utils.ts"; import { mockUser1, mockUser2, mockUser3 } from "../mocks.ts"; import type { User } from "../models.ts"; @@ -9,14 +9,17 @@ Deno.test("indexable_collection - history", async (t) => { "Should persist history of multiple inserts in correct order", async () => { await useKv(async (kv) => { - const db = kvdex(kv, { - users: collection(model(), { - history: true, - indices: { - username: "primary", - age: "secondary", - }, - }), + const db = kvdex({ + kv, + schema: { + users: collection(model(), { + history: true, + indices: { + username: "primary", + age: "secondary", + }, + }), + }, }); const id = "id"; @@ -43,14 +46,17 @@ Deno.test("indexable_collection - history", async (t) => { "Should persist history of multiple inserts in correct order after deleting", async () => { await useKv(async (kv) => { - const db = kvdex(kv, { - users: collection(model(), { - history: true, - indices: { - username: "primary", - age: "secondary", - }, - }), + const db = kvdex({ + kv, + schema: { + users: collection(model(), { + history: true, + indices: { + username: "primary", + age: "secondary", + }, + }), + }, }); const id = "id"; @@ -85,14 +91,17 @@ Deno.test("indexable_collection - history", async (t) => { "Should persist history of multiple inserts and updates in correct order", async () => { await useKv(async (kv) => { - const db = kvdex(kv, { - users: collection(model(), { - history: true, - indices: { - username: "primary", - age: "secondary", - }, - }), + const db = kvdex({ + kv, + schema: { + users: collection(model(), { + history: true, + indices: { + username: "primary", + age: "secondary", + }, + }), + }, }); const id = "id"; @@ -119,14 +128,17 @@ Deno.test("indexable_collection - history", async (t) => { "Should persist version history of insert and delete by deleteMany()", async () => { await useKv(async (kv) => { - const db = kvdex(kv, { - users: collection(model(), { - history: true, - indices: { - username: "primary", - age: "secondary", - }, - }), + const db = kvdex({ + kv, + schema: { + users: collection(model(), { + history: true, + indices: { + username: "primary", + age: "secondary", + }, + }), + }, }); const id = "id"; @@ -156,13 +168,16 @@ Deno.test("indexable_collection - history", async (t) => { "Should not find history", async () => { await useKv(async (kv) => { - const db = kvdex(kv, { - users: collection(model(), { - indices: { - username: "primary", - age: "secondary", - }, - }), + const db = kvdex({ + kv, + schema: { + users: collection(model(), { + indices: { + username: "primary", + age: "secondary", + }, + }), + }, }); const id = "id"; @@ -179,14 +194,17 @@ Deno.test("indexable_collection - history", async (t) => { await t.step("Should find filtered history", async () => { await useKv(async (kv) => { - const db = kvdex(kv, { - users: collection(model(), { - history: true, - indices: { - username: "primary", - age: "secondary", - }, - }), + const db = kvdex({ + kv, + schema: { + users: collection(model(), { + history: true, + indices: { + username: "primary", + age: "secondary", + }, + }), + }, }); const id = "id"; @@ -223,14 +241,17 @@ Deno.test("indexable_collection - history", async (t) => { await t.step("Should delete all document history", async () => { await useKv(async (kv) => { - const db = kvdex(kv, { - users: collection(model(), { - history: true, - indices: { - username: "primary", - age: "secondary", - }, - }), + const db = kvdex({ + kv, + schema: { + users: collection(model(), { + history: true, + indices: { + username: "primary", + age: "secondary", + }, + }), + }, }); const id = "id"; diff --git a/tests/indexable_collection/listenQueue.test.ts b/tests/indexable_collection/listenQueue.test.ts index 17ed319..f1f2a2d 100644 --- a/tests/indexable_collection/listenQueue.test.ts +++ b/tests/indexable_collection/listenQueue.test.ts @@ -10,7 +10,7 @@ import { UNDELIVERED_KEY_PREFIX, } from "../../src/constants.ts"; import { createHandlerId, extendKey } from "../../src/utils.ts"; -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import type { User } from "../models.ts"; import { createResolver, sleep, useKv } from "../utils.ts"; @@ -20,8 +20,9 @@ Deno.test("indexable_collection - listenQueue", async (t) => { const data = "data"; const undeliveredId = "id"; - const db = kvdex(kv, { - i_users: collection(model(), { indices: {} }), + const db = kvdex({ + kv, + schema: { i_users: collection(model(), { indices: {} }) }, }); const sleeper = createResolver(); @@ -60,8 +61,9 @@ Deno.test("indexable_collection - listenQueue", async (t) => { await t.step("Should not receive db queue message", async () => { await useKv(async (kv) => { - const db = kvdex(kv, { - i_users: collection(model(), { indices: {} }), + const db = kvdex({ + kv, + schema: { i_users: collection(model(), { indices: {} }) }, }); let assertion = true; diff --git a/tests/indexable_collection/map.test.ts b/tests/indexable_collection/map.test.ts index ac10a62..adc8a9a 100644 --- a/tests/indexable_collection/map.test.ts +++ b/tests/indexable_collection/map.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { generateUsers, useDb } from "../utils.ts"; Deno.test("indexable_collection - map", async (t) => { diff --git a/tests/indexable_collection/mapBySecondaryIndex.test.ts b/tests/indexable_collection/mapBySecondaryIndex.test.ts index f89a874..1ac610d 100644 --- a/tests/indexable_collection/mapBySecondaryIndex.test.ts +++ b/tests/indexable_collection/mapBySecondaryIndex.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1, mockUser2, mockUser3 } from "../mocks.ts"; import { useDb } from "../utils.ts"; diff --git a/tests/indexable_collection/mapBySecondaryOrder.test.ts b/tests/indexable_collection/mapBySecondaryOrder.test.ts index 973ac1a..0777ca0 100644 --- a/tests/indexable_collection/mapBySecondaryOrder.test.ts +++ b/tests/indexable_collection/mapBySecondaryOrder.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1, mockUser2, diff --git a/tests/indexable_collection/properties.test.ts b/tests/indexable_collection/properties.test.ts index b20ccc7..50f6df2 100644 --- a/tests/indexable_collection/properties.test.ts +++ b/tests/indexable_collection/properties.test.ts @@ -12,7 +12,7 @@ import { SECONDARY_INDEX_KEY_PREFIX, } from "../../src/constants.ts"; import { extendKey, keyEq } from "../../src/utils.ts"; -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1 } from "../mocks.ts"; import type { User } from "../models.ts"; import { generateUsers, sleep, useDb, useKv } from "../utils.ts"; @@ -41,15 +41,18 @@ Deno.test("indexable_collection - properties", async (t) => { await t.step("Should generate ids with custom id generator", async () => { await useKv((kv) => { - const db = kvdex(kv, { - users1: collection(model(), { - idGenerator: () => Math.random(), - indices: {}, - }), - users2: collection(model(), { - idGenerator: (data) => data.username, - indices: {}, - }), + const db = kvdex({ + kv, + schema: { + users1: collection(model(), { + idGenerator: () => Math.random(), + indices: {}, + }), + users2: collection(model(), { + idGenerator: (data) => data.username, + indices: {}, + }), + }, }); const id1 = db.users1._idGenerator(mockUser1); @@ -232,24 +235,27 @@ Deno.test("indexable_collection - properties", async (t) => { await t.step("Should allow optional indices", async () => { await useKv(async (kv) => { - const db = kvdex(kv, { - i: collection( - model<{ - oblPrimary: string; - oblSecondary: number; - optPrimary?: string; - optSecondary?: number; - check?: Date; - }>(), - { - indices: { - oblPrimary: "primary", - oblSecondary: "secondary", - optPrimary: "primary", - optSecondary: "secondary", + const db = kvdex({ + kv, + schema: { + i: collection( + model<{ + oblPrimary: string; + oblSecondary: number; + optPrimary?: string; + optSecondary?: number; + check?: Date; + }>(), + { + indices: { + oblPrimary: "primary", + oblSecondary: "secondary", + optPrimary: "primary", + optSecondary: "secondary", + }, }, - }, - ), + ), + }, }); const cr1 = await db.i.add({ @@ -459,7 +465,6 @@ Deno.test("indexable_collection - properties", async (t) => { val20, val21, val22, - val23, }; const val25 = new Set(val23); const val26 = new Map([ @@ -487,163 +492,166 @@ Deno.test("indexable_collection - properties", async (t) => { ["val22", val22], ]); - const db = kvdex(kv, { - val1: collection(model(), { - indices: { - p: "primary", - s: "secondary", - }, - }), - val2: collection(model(), { - indices: { - p: "primary", - s: "secondary", - }, - }), - val3: collection(model(), { - indices: { - p: "primary", - s: "secondary", - }, - }), - val4: collection(model(), { - indices: { - p: "primary", - s: "secondary", - }, - }), - val5: collection(model(), { - indices: { - p: "primary", - s: "secondary", - }, - }), - val6: collection(model(), { - indices: { - p: "primary", - s: "secondary", - }, - }), - val7: collection(model(), { - indices: { - p: "primary", - s: "secondary", - }, - }), - val8: collection(model(), { - indices: { - p: "primary", - s: "secondary", - }, - }), - val9: collection(model(), { - indices: { - p: "primary", - s: "secondary", - }, - }), - val10: collection(model(), { - indices: { - p: "primary", - s: "secondary", - }, - }), - val11: collection(model(), { - indices: { - p: "primary", - s: "secondary", - }, - }), - val12: collection(model(), { - indices: { - p: "primary", - s: "secondary", - }, - }), - val13: collection(model(), { - indices: { - p: "primary", - s: "secondary", - }, - }), - val14: collection(model(), { - indices: { - p: "primary", - s: "secondary", - }, - }), - val15: collection(model(), { - indices: { - p: "primary", - s: "secondary", - }, - }), - val16: collection(model(), { - indices: { - p: "primary", - s: "secondary", - }, - }), - val17: collection(model(), { - indices: { - p: "primary", - s: "secondary", - }, - }), - val18: collection(model(), { - indices: { - p: "primary", - s: "secondary", - }, - }), - val19: collection(model(), { - indices: { - p: "primary", - s: "secondary", - }, - }), - val20: collection(model(), { - indices: { - p: "primary", - s: "secondary", - }, - }), - val21: collection(model(), { - indices: { - p: "primary", - s: "secondary", - }, - }), - val22: collection(model(), { - indices: { - p: "primary", - s: "secondary", - }, - }), - val23: collection(model(), { - indices: { - p: "primary", - s: "secondary", - }, - }), - val24: collection(model(), { - indices: { - p: "primary", - s: "secondary", - }, - }), - val25: collection(model(), { - indices: { - p: "primary", - s: "secondary", - }, - }), - val26: collection(model(), { - indices: { - p: "primary", - s: "secondary", - }, - }), + const db = kvdex({ + kv, + schema: { + val1: collection(model(), { + indices: { + p: "primary", + s: "secondary", + }, + }), + val2: collection(model(), { + indices: { + p: "primary", + s: "secondary", + }, + }), + val3: collection(model(), { + indices: { + p: "primary", + s: "secondary", + }, + }), + val4: collection(model(), { + indices: { + p: "primary", + s: "secondary", + }, + }), + val5: collection(model(), { + indices: { + p: "primary", + s: "secondary", + }, + }), + val6: collection(model(), { + indices: { + p: "primary", + s: "secondary", + }, + }), + val7: collection(model(), { + indices: { + p: "primary", + s: "secondary", + }, + }), + val8: collection(model(), { + indices: { + p: "primary", + s: "secondary", + }, + }), + val9: collection(model(), { + indices: { + p: "primary", + s: "secondary", + }, + }), + val10: collection(model(), { + indices: { + p: "primary", + s: "secondary", + }, + }), + val11: collection(model(), { + indices: { + p: "primary", + s: "secondary", + }, + }), + val12: collection(model(), { + indices: { + p: "primary", + s: "secondary", + }, + }), + val13: collection(model(), { + indices: { + p: "primary", + s: "secondary", + }, + }), + val14: collection(model(), { + indices: { + p: "primary", + s: "secondary", + }, + }), + val15: collection(model(), { + indices: { + p: "primary", + s: "secondary", + }, + }), + val16: collection(model(), { + indices: { + p: "primary", + s: "secondary", + }, + }), + val17: collection(model(), { + indices: { + p: "primary", + s: "secondary", + }, + }), + val18: collection(model(), { + indices: { + p: "primary", + s: "secondary", + }, + }), + val19: collection(model(), { + indices: { + p: "primary", + s: "secondary", + }, + }), + val20: collection(model(), { + indices: { + p: "primary", + s: "secondary", + }, + }), + val21: collection(model(), { + indices: { + p: "primary", + s: "secondary", + }, + }), + val22: collection(model(), { + indices: { + p: "primary", + s: "secondary", + }, + }), + val23: collection(model(), { + indices: { + p: "primary", + s: "secondary", + }, + }), + val24: collection(model(), { + indices: { + p: "primary", + s: "secondary", + }, + }), + val25: collection(model(), { + indices: { + p: "primary", + s: "secondary", + }, + }), + val26: collection(model(), { + indices: { + p: "primary", + s: "secondary", + }, + }), + }, }); const cr1 = await db.val1.add({ p: val1, s: val1 }); @@ -891,20 +899,23 @@ Deno.test("indexable_collection - properties", async (t) => { await t.step("Should successfully generate id asynchronously", async () => { await useKv(async (kv) => { - const db = kvdex(kv, { - test: collection(model(), { - indices: { - username: "primary", - age: "secondary", - }, - idGenerator: async (user) => { - const buffer = await crypto.subtle.digest( - "SHA-256", - new ArrayBuffer(user.age), - ); - return Math.random() * buffer.byteLength; - }, - }), + const db = kvdex({ + kv, + schema: { + test: collection(model(), { + indices: { + username: "primary", + age: "secondary", + }, + idGenerator: async (user) => { + const buffer = await crypto.subtle.digest( + "SHA-256", + new ArrayBuffer(user.age), + ); + return Math.random() * buffer.byteLength; + }, + }), + }, }); const cr1 = await db.test.add(mockUser1); diff --git a/tests/indexable_collection/set.test.ts b/tests/indexable_collection/set.test.ts index bfe4dd8..99a750f 100644 --- a/tests/indexable_collection/set.test.ts +++ b/tests/indexable_collection/set.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1, mockUser2, mockUserInvalid } from "../mocks.ts"; import { useDb } from "../utils.ts"; diff --git a/tests/indexable_collection/types.test.ts b/tests/indexable_collection/types.test.ts index fdcd998..071044c 100644 --- a/tests/indexable_collection/types.test.ts +++ b/tests/indexable_collection/types.test.ts @@ -1,5 +1,5 @@ import { collection, kvdex, model } from "../../mod.ts"; -import { assert, assertEquals } from "../test.deps.ts"; +import { assert, assertEquals } from "@std/assert"; import { useKv } from "../utils.ts"; import { TObject } from "../values.ts"; @@ -8,13 +8,16 @@ Deno.test("indexable_collection - types", async (t) => { "Should allow and properly store/retrieve all KvValue types", async () => { await useKv(async (kv) => { - const db = kvdex(kv, { - objects: collection(model(), { - indices: { - TString: "primary", - TNumber: "secondary", - }, - }), + const db = kvdex({ + kv, + schema: { + objects: collection(model(), { + indices: { + TString: "primary", + TNumber: "secondary", + }, + }), + }, }); const cr = await db.objects.add(TObject); diff --git a/tests/indexable_collection/update.test.ts b/tests/indexable_collection/update.test.ts index 60a7d87..b9796f3 100644 --- a/tests/indexable_collection/update.test.ts +++ b/tests/indexable_collection/update.test.ts @@ -1,5 +1,5 @@ import type { Document } from "../../mod.ts"; -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1, mockUser2, mockUserInvalid } from "../mocks.ts"; import type { User } from "../models.ts"; import { useDb } from "../utils.ts"; diff --git a/tests/indexable_collection/updateByPrimaryIndex.test.ts b/tests/indexable_collection/updateByPrimaryIndex.test.ts index ffb8ac6..04a63ce 100644 --- a/tests/indexable_collection/updateByPrimaryIndex.test.ts +++ b/tests/indexable_collection/updateByPrimaryIndex.test.ts @@ -1,5 +1,5 @@ import type { Document } from "../../mod.ts"; -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1, mockUser2, mockUserInvalid } from "../mocks.ts"; import type { User } from "../models.ts"; import { useDb } from "../utils.ts"; diff --git a/tests/indexable_collection/updateBySecondaryIndex.test.ts b/tests/indexable_collection/updateBySecondaryIndex.test.ts index 0775b85..636c4a7 100644 --- a/tests/indexable_collection/updateBySecondaryIndex.test.ts +++ b/tests/indexable_collection/updateBySecondaryIndex.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1, mockUserInvalid } from "../mocks.ts"; import { generateUsers, useDb } from "../utils.ts"; diff --git a/tests/indexable_collection/updateMany.test.ts b/tests/indexable_collection/updateMany.test.ts index 28e61bb..d484ba2 100644 --- a/tests/indexable_collection/updateMany.test.ts +++ b/tests/indexable_collection/updateMany.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1, mockUserInvalid } from "../mocks.ts"; import { generateUsers, useDb } from "../utils.ts"; diff --git a/tests/indexable_collection/updateManyBySecondaryOrder.test.ts b/tests/indexable_collection/updateManyBySecondaryOrder.test.ts index 4b0ddba..2286e05 100644 --- a/tests/indexable_collection/updateManyBySecondaryOrder.test.ts +++ b/tests/indexable_collection/updateManyBySecondaryOrder.test.ts @@ -1,4 +1,4 @@ -import { assert, assertEquals } from "../test.deps.ts"; +import { assert, assertEquals } from "@std/assert"; import { mockUser1, mockUser2, @@ -8,6 +8,7 @@ import { import { generateUsers, useDb } from "../utils.ts"; import type { User } from "../models.ts"; +// TODO: fix update document deleting indices even when failing to update document Deno.test.ignore( "indexable_collection - updateManyBySecondaryOrder", async (t) => { diff --git a/tests/indexable_collection/updateOne.test.ts b/tests/indexable_collection/updateOne.test.ts index 0a0896e..e6cd6a3 100644 --- a/tests/indexable_collection/updateOne.test.ts +++ b/tests/indexable_collection/updateOne.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1, mockUser2, mockUser3, mockUserInvalid } from "../mocks.ts"; import { sleep, useDb } from "../utils.ts"; diff --git a/tests/indexable_collection/updateOneBySecondaryIndex.test.ts b/tests/indexable_collection/updateOneBySecondaryIndex.test.ts index 11cb078..5e26d10 100644 --- a/tests/indexable_collection/updateOneBySecondaryIndex.test.ts +++ b/tests/indexable_collection/updateOneBySecondaryIndex.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1, mockUser2, mockUser3, mockUserInvalid } from "../mocks.ts"; import { sleep, useDb } from "../utils.ts"; diff --git a/tests/indexable_collection/updateOneBySecondaryOrder.test.ts b/tests/indexable_collection/updateOneBySecondaryOrder.test.ts index 9020442..c96ada0 100644 --- a/tests/indexable_collection/updateOneBySecondaryOrder.test.ts +++ b/tests/indexable_collection/updateOneBySecondaryOrder.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1, mockUser2, diff --git a/tests/indexable_collection/upsert.test.ts b/tests/indexable_collection/upsert.test.ts index 070c4a6..65f1223 100644 --- a/tests/indexable_collection/upsert.test.ts +++ b/tests/indexable_collection/upsert.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1, mockUser2, mockUser3 } from "../mocks.ts"; import type { User } from "../models.ts"; import { useDb } from "../utils.ts"; diff --git a/tests/indexable_collection/upsertByPrimaryIndex.test.ts b/tests/indexable_collection/upsertByPrimaryIndex.test.ts index ab957ea..c4950ce 100644 --- a/tests/indexable_collection/upsertByPrimaryIndex.test.ts +++ b/tests/indexable_collection/upsertByPrimaryIndex.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1, mockUser2, mockUser3 } from "../mocks.ts"; import type { User } from "../models.ts"; import { useDb } from "../utils.ts"; diff --git a/tests/indexable_collection/watch.test.ts b/tests/indexable_collection/watch.test.ts index 1cacfde..ec8c0d4 100644 --- a/tests/indexable_collection/watch.test.ts +++ b/tests/indexable_collection/watch.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1, mockUser2, mockUser3 } from "../mocks.ts"; import { sleep, useDb } from "../utils.ts"; import type { User } from "../models.ts"; diff --git a/tests/indexable_collection/watchMany.test.ts b/tests/indexable_collection/watchMany.test.ts index 5f2e7dc..c6a5975 100644 --- a/tests/indexable_collection/watchMany.test.ts +++ b/tests/indexable_collection/watchMany.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1, mockUser2, mockUser3 } from "../mocks.ts"; import { generateUsers, sleep, useDb } from "../utils.ts"; import type { User } from "../models.ts"; diff --git a/tests/models.ts b/tests/models.ts index 8f9f618..7e9db27 100644 --- a/tests/models.ts +++ b/tests/models.ts @@ -1,5 +1,5 @@ import { model } from "../mod.ts"; -import { z } from "./test.deps.ts"; +import { z } from "zod"; export type Address = z.infer; diff --git a/tests/serialized_collection/add.test.ts b/tests/serialized_collection/add.test.ts index 9170edf..8c2d02a 100644 --- a/tests/serialized_collection/add.test.ts +++ b/tests/serialized_collection/add.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUserInvalid } from "../mocks.ts"; import { generateLargeUsers, useDb } from "../utils.ts"; diff --git a/tests/serialized_collection/addMany.test.ts b/tests/serialized_collection/addMany.test.ts index 3662fa8..bfc8185 100644 --- a/tests/serialized_collection/addMany.test.ts +++ b/tests/serialized_collection/addMany.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { generateInvalidUsers, generateLargeUsers, useDb } from "../utils.ts"; Deno.test("serialized_collection - addMany", async (t) => { diff --git a/tests/serialized_collection/count.test.ts b/tests/serialized_collection/count.test.ts index da6e321..47c2897 100644 --- a/tests/serialized_collection/count.test.ts +++ b/tests/serialized_collection/count.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { generateLargeUsers, useDb } from "../utils.ts"; Deno.test("serialized_collection - count", async (t) => { diff --git a/tests/serialized_collection/delete.test.ts b/tests/serialized_collection/delete.test.ts index c6e1a39..5398c59 100644 --- a/tests/serialized_collection/delete.test.ts +++ b/tests/serialized_collection/delete.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1 } from "../mocks.ts"; import { generateLargeUsers, useDb } from "../utils.ts"; diff --git a/tests/serialized_collection/deleteMany.test.ts b/tests/serialized_collection/deleteMany.test.ts index bb3df78..287b642 100644 --- a/tests/serialized_collection/deleteMany.test.ts +++ b/tests/serialized_collection/deleteMany.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { generateLargeUsers, useDb } from "../utils.ts"; Deno.test("serialized_collection - deleteMany", async (t) => { diff --git a/tests/serialized_collection/enqueue.test.ts b/tests/serialized_collection/enqueue.test.ts index e22247e..ccbb42f 100644 --- a/tests/serialized_collection/enqueue.test.ts +++ b/tests/serialized_collection/enqueue.test.ts @@ -6,9 +6,9 @@ import { type QueueMessage, } from "../../mod.ts"; import { createHandlerId } from "../../src/utils.ts"; -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import type { User } from "../models.ts"; -import { createResolver, useDb, useKv } from "../utils.ts"; +import { createResolver, testEncoder, useDb, useKv } from "../utils.ts"; Deno.test("serialized_collection - enqueue", async (t) => { await t.step("Should enqueue message with string data", async () => { @@ -16,8 +16,11 @@ Deno.test("serialized_collection - enqueue", async (t) => { const data = "data"; const undeliveredId = "undelivered"; - const db = kvdex(kv, { - s_users: collection(model(), { serialize: "json" }), + const db = kvdex({ + kv, + schema: { + s_users: collection(model(), { encoder: testEncoder }), + }, }); const sleeper = createResolver(); diff --git a/tests/serialized_collection/find.test.ts b/tests/serialized_collection/find.test.ts index d85b5af..9aa2dcf 100644 --- a/tests/serialized_collection/find.test.ts +++ b/tests/serialized_collection/find.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1 } from "../mocks.ts"; import { useDb } from "../utils.ts"; diff --git a/tests/serialized_collection/findMany.test.ts b/tests/serialized_collection/findMany.test.ts index 9c0fe24..ebb0eae 100644 --- a/tests/serialized_collection/findMany.test.ts +++ b/tests/serialized_collection/findMany.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { generateLargeUsers, useDb } from "../utils.ts"; Deno.test("serialized_collection - findMany", async (t) => { diff --git a/tests/serialized_collection/forEach.test.ts b/tests/serialized_collection/forEach.test.ts index 8af7a05..836a820 100644 --- a/tests/serialized_collection/forEach.test.ts +++ b/tests/serialized_collection/forEach.test.ts @@ -1,5 +1,5 @@ import type { Document } from "../../mod.ts"; -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import type { User } from "../models.ts"; import { generateLargeUsers, useDb } from "../utils.ts"; diff --git a/tests/serialized_collection/getMany.test.ts b/tests/serialized_collection/getMany.test.ts index 0696af7..20e9552 100644 --- a/tests/serialized_collection/getMany.test.ts +++ b/tests/serialized_collection/getMany.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { generateLargeUsers, useDb } from "../utils.ts"; Deno.test("serialized_collection - getMany", async (t) => { diff --git a/tests/serialized_collection/getOne.test.ts b/tests/serialized_collection/getOne.test.ts index 4664886..a847fb7 100644 --- a/tests/serialized_collection/getOne.test.ts +++ b/tests/serialized_collection/getOne.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { sleep, useDb } from "../utils.ts"; import { mockUser1, mockUser2 } from "../mocks.ts"; diff --git a/tests/serialized_collection/history.test.ts b/tests/serialized_collection/history.test.ts index 42f567e..ecf60ec 100644 --- a/tests/serialized_collection/history.test.ts +++ b/tests/serialized_collection/history.test.ts @@ -1,19 +1,23 @@ import { collection, kvdex, model } from "../../mod.ts"; -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { sleep, useKv } from "../utils.ts"; import { mockUser1, mockUser2, mockUser3 } from "../mocks.ts"; import type { User } from "../models.ts"; +import { jsonEncoder } from "../../src/ext/encoding/mod.ts"; Deno.test("serialized_collection - history", async (t) => { await t.step( "Should persist history of multiple inserts in correct order", async () => { await useKv(async (kv) => { - const db = kvdex(kv, { - users: collection(model(), { - history: true, - serialize: "json", - }), + const db = kvdex({ + kv, + schema: { + users: collection(model(), { + history: true, + encoder: jsonEncoder(), + }), + }, }); const id = "id"; @@ -40,11 +44,14 @@ Deno.test("serialized_collection - history", async (t) => { "Should persist history of multiple inserts in correct order after deleting", async () => { await useKv(async (kv) => { - const db = kvdex(kv, { - users: collection(model(), { - history: true, - serialize: "json", - }), + const db = kvdex({ + kv, + schema: { + users: collection(model(), { + history: true, + encoder: jsonEncoder(), + }), + }, }); const id = "id"; @@ -79,11 +86,14 @@ Deno.test("serialized_collection - history", async (t) => { "Should persist history of multiple inserts and updates in correct order", async () => { await useKv(async (kv) => { - const db = kvdex(kv, { - users: collection(model(), { - history: true, - serialize: "json", - }), + const db = kvdex({ + kv, + schema: { + users: collection(model(), { + history: true, + encoder: jsonEncoder(), + }), + }, }); const id = "id"; @@ -110,11 +120,14 @@ Deno.test("serialized_collection - history", async (t) => { "Should persist version history of insert and delete by deleteMany()", async () => { await useKv(async (kv) => { - const db = kvdex(kv, { - users: collection(model(), { - history: true, - serialize: "json", - }), + const db = kvdex({ + kv, + schema: { + users: collection(model(), { + history: true, + encoder: jsonEncoder(), + }), + }, }); const id = "id"; @@ -144,8 +157,9 @@ Deno.test("serialized_collection - history", async (t) => { "Should not find history", async () => { await useKv(async (kv) => { - const db = kvdex(kv, { - users: collection(model()), + const db = kvdex({ + kv, + schema: { users: collection(model()) }, }); const id = "id"; @@ -162,11 +176,14 @@ Deno.test("serialized_collection - history", async (t) => { await t.step("Should find filtered history", async () => { await useKv(async (kv) => { - const db = kvdex(kv, { - users: collection(model(), { - history: true, - serialize: "json", - }), + const db = kvdex({ + kv, + schema: { + users: collection(model(), { + history: true, + encoder: jsonEncoder(), + }), + }, }); const id = "id"; @@ -203,11 +220,14 @@ Deno.test("serialized_collection - history", async (t) => { await t.step("Should delete all document history", async () => { await useKv(async (kv) => { - const db = kvdex(kv, { - users: collection(model(), { - serialize: "json", - history: true, - }), + const db = kvdex({ + kv, + schema: { + users: collection(model(), { + encoder: jsonEncoder(), + history: true, + }), + }, }); const id = "id"; diff --git a/tests/serialized_collection/listenQueue.test.ts b/tests/serialized_collection/listenQueue.test.ts index 366503c..027effd 100644 --- a/tests/serialized_collection/listenQueue.test.ts +++ b/tests/serialized_collection/listenQueue.test.ts @@ -10,7 +10,7 @@ import { UNDELIVERED_KEY_PREFIX, } from "../../src/constants.ts"; import { createHandlerId, extendKey } from "../../src/utils.ts"; -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import type { User } from "../models.ts"; import { createResolver, sleep, useKv } from "../utils.ts"; @@ -20,8 +20,9 @@ Deno.test("serialized_collection - listenQueue", async (t) => { const data = "data"; const undeliveredId = "id"; - const db = kvdex(kv, { - s_users: collection(model()), + const db = kvdex({ + kv, + schema: { s_users: collection(model()) }, }); const sleeper = createResolver(); @@ -61,8 +62,9 @@ Deno.test("serialized_collection - listenQueue", async (t) => { await t.step("Should not receive db queue message", async () => { await useKv(async (kv) => { - const db = kvdex(kv, { - l_users: collection(model()), + const db = kvdex({ + kv, + schema: { l_users: collection(model()) }, }); let assertion = true; diff --git a/tests/serialized_collection/map.test.ts b/tests/serialized_collection/map.test.ts index 46e06cf..b86f063 100644 --- a/tests/serialized_collection/map.test.ts +++ b/tests/serialized_collection/map.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { generateLargeUsers, useDb } from "../utils.ts"; Deno.test("serialized_collection - map", async (t) => { diff --git a/tests/serialized_collection/properties.test.ts b/tests/serialized_collection/properties.test.ts index 456c802..6607d01 100644 --- a/tests/serialized_collection/properties.test.ts +++ b/tests/serialized_collection/properties.test.ts @@ -5,12 +5,13 @@ import { SEGMENT_KEY_PREFIX, } from "../../src/constants.ts"; import { extendKey, keyEq } from "../../src/utils.ts"; -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1, mockUser2 } from "../mocks.ts"; import type { User } from "../models.ts"; import { generateLargeUsers, useDb, useKv } from "../utils.ts"; import { mockUser3 } from "../mocks.ts"; import { sleep } from "../utils.ts"; +import { jsonEncoder } from "../../src/ext/encoding/mod.ts"; Deno.test("serialized_collection - properties", async (t) => { await t.step("Keys should have the correct prefixes", async () => { @@ -28,15 +29,18 @@ Deno.test("serialized_collection - properties", async (t) => { await t.step("Should generate ids with custom id generator", async () => { await useKv((kv) => { - const db = kvdex(kv, { - users1: collection(model(), { - serialize: "json", - idGenerator: () => Math.random(), - }), - users2: collection(model(), { - serialize: "json", - idGenerator: (data) => data.username, - }), + const db = kvdex({ + kv, + schema: { + users1: collection(model(), { + encoder: jsonEncoder(), + idGenerator: () => Math.random(), + }), + users2: collection(model(), { + encoder: jsonEncoder(), + idGenerator: (data) => data.username, + }), + }, }); const id1 = db.users1._idGenerator(mockUser1); @@ -288,17 +292,20 @@ Deno.test("serialized_collection - properties", async (t) => { await t.step("Should successfully generate id asynchronously", async () => { await useKv(async (kv) => { - const db = kvdex(kv, { - test: collection(model(), { - serialize: "json", - idGenerator: async (user) => { - const buffer = await crypto.subtle.digest( - "SHA-256", - new ArrayBuffer(user.age), - ); - return Math.random() * buffer.byteLength; - }, - }), + const db = kvdex({ + kv, + schema: { + test: collection(model(), { + encoder: jsonEncoder(), + idGenerator: async (user) => { + const buffer = await crypto.subtle.digest( + "SHA-256", + new ArrayBuffer(user.age), + ); + return Math.random() * buffer.byteLength; + }, + }), + }, }); const cr1 = await db.test.add(mockUser1); diff --git a/tests/serialized_collection/set.test.ts b/tests/serialized_collection/set.test.ts index 50d4571..2d6ccb5 100644 --- a/tests/serialized_collection/set.test.ts +++ b/tests/serialized_collection/set.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1, mockUser2, mockUserInvalid } from "../mocks.ts"; import { useDb } from "../utils.ts"; diff --git a/tests/serialized_collection/types.test.ts b/tests/serialized_collection/types.test.ts index 0bbb78e..5ceaccc 100644 --- a/tests/serialized_collection/types.test.ts +++ b/tests/serialized_collection/types.test.ts @@ -1,5 +1,6 @@ import { collection, kvdex, model } from "../../mod.ts"; -import { assert, assertEquals } from "../test.deps.ts"; +import { jsonEncoder } from "../../src/ext/encoding/mod.ts"; +import { assert, assertEquals } from "@std/assert"; import { useKv } from "../utils.ts"; import { VALUES } from "../values.ts"; @@ -12,10 +13,13 @@ Deno.test("serialized_collection - types", async (t) => { VALUES.map(( val, i, - ) => [i, collection(model(), { serialize: "v8" })]), + ) => [ + i, + collection(model(), { encoder: jsonEncoder() }), + ]), ); - const db = kvdex(kv, schema); + const db = kvdex({ kv, schema }); const crs = await Promise.all(VALUES.map((val, i) => db[i].add(val))); assert(crs.every((cr) => cr.ok)); diff --git a/tests/serialized_collection/update.test.ts b/tests/serialized_collection/update.test.ts index ba87873..bfca046 100644 --- a/tests/serialized_collection/update.test.ts +++ b/tests/serialized_collection/update.test.ts @@ -1,5 +1,5 @@ import { collection, kvdex, model } from "../../mod.ts"; -import { assert, assertEquals } from "../test.deps.ts"; +import { assert, assertEquals } from "@std/assert"; import { mockUser1, mockUser2, mockUserInvalid } from "../mocks.ts"; import { useDb, useKv } from "../utils.ts"; @@ -108,10 +108,13 @@ Deno.test("serialized_collection - update", async (t) => { "Should update documents of type Array, Set and Map using merge", async () => { await useKv(async (kv) => { - const db = kvdex(kv, { - arrays: collection(model()), - sets: collection(model>()), - maps: collection(model>()), + const db = kvdex({ + kv, + schema: { + arrays: collection(model()), + sets: collection(model>()), + maps: collection(model>()), + }, }); const val1 = [1, 2, 4]; @@ -168,10 +171,13 @@ Deno.test("serialized_collection - update", async (t) => { "Should update documents of primitive and built-in object types using replace", async () => { await useKv(async (kv) => { - const db = kvdex(kv, { - numbers: collection(model()), - strings: collection(model()), - dates: collection(model()), + const db = kvdex({ + kv, + schema: { + numbers: collection(model()), + strings: collection(model()), + dates: collection(model()), + }, }); const cr1 = await db.numbers.add(10); diff --git a/tests/serialized_collection/updateMany.test.ts b/tests/serialized_collection/updateMany.test.ts index b138084..ecd1ea2 100644 --- a/tests/serialized_collection/updateMany.test.ts +++ b/tests/serialized_collection/updateMany.test.ts @@ -1,5 +1,5 @@ import { collection, kvdex, model } from "../../mod.ts"; -import { assert, assertEquals } from "../test.deps.ts"; +import { assert, assertEquals } from "@std/assert"; import { mockUser1, mockUserInvalid } from "../mocks.ts"; import { generateNumbers, generateUsers, useDb, useKv } from "../utils.ts"; @@ -123,10 +123,13 @@ Deno.test("serialized_collection - updateMany", async (t) => { "Should update 1000 documents of type Array, Set and Map using merge", async () => { await useKv(async (kv) => { - const db = kvdex(kv, { - arrays: collection(model()), - sets: collection(model>()), - maps: collection(model>()), + const db = kvdex({ + kv, + schema: { + arrays: collection(model()), + sets: collection(model>()), + maps: collection(model>()), + }, }); const val1 = [1, 2, 4]; @@ -197,10 +200,13 @@ Deno.test("serialized_collection - updateMany", async (t) => { "Should update 1000 documents of types primitive and built-in object using replace", async () => { await useKv(async (kv) => { - const db = kvdex(kv, { - numbers: collection(model()), - strings: collection(model()), - dates: collection(model()), + const db = kvdex({ + kv, + schema: { + numbers: collection(model()), + strings: collection(model()), + dates: collection(model()), + }, }); const numbers = generateNumbers(1_000); diff --git a/tests/serialized_collection/updateOne.test.ts b/tests/serialized_collection/updateOne.test.ts index 35c35e5..3164176 100644 --- a/tests/serialized_collection/updateOne.test.ts +++ b/tests/serialized_collection/updateOne.test.ts @@ -1,5 +1,5 @@ import { collection, kvdex, model } from "../../mod.ts"; -import { assert, assertEquals, assertNotEquals } from "../test.deps.ts"; +import { assert, assertEquals, assertNotEquals } from "@std/assert"; import { mockUser1, mockUser2, mockUser3, mockUserInvalid } from "../mocks.ts"; import { generateNumbers, @@ -144,10 +144,13 @@ Deno.test("serialized_collection - updateOne", async (t) => { "Should update only one document of type Array, Set and Map using merge", async () => { await useKv(async (kv) => { - const db = kvdex(kv, { - arrays: collection(model()), - sets: collection(model>()), - maps: collection(model>()), + const db = kvdex({ + kv, + schema: { + arrays: collection(model()), + sets: collection(model>()), + maps: collection(model>()), + }, }); const val1 = [1, 2, 4]; @@ -218,10 +221,13 @@ Deno.test("serialized_collection - updateOne", async (t) => { "Should update only one document of types primitive and built-in object using replace", async () => { await useKv(async (kv) => { - const db = kvdex(kv, { - numbers: collection(model()), - strings: collection(model()), - dates: collection(model()), + const db = kvdex({ + kv, + schema: { + numbers: collection(model()), + strings: collection(model()), + dates: collection(model()), + }, }); const numbers = generateNumbers(1_000); diff --git a/tests/serialized_collection/upsert.test.ts b/tests/serialized_collection/upsert.test.ts index a5b5259..ef41c58 100644 --- a/tests/serialized_collection/upsert.test.ts +++ b/tests/serialized_collection/upsert.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1, mockUser2, mockUser3 } from "../mocks.ts"; import type { User } from "../models.ts"; import { useDb } from "../utils.ts"; diff --git a/tests/serialized_collection/watch.test.ts b/tests/serialized_collection/watch.test.ts index 98ab772..b905be1 100644 --- a/tests/serialized_collection/watch.test.ts +++ b/tests/serialized_collection/watch.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1, mockUser2, mockUser3 } from "../mocks.ts"; import { sleep, useDb } from "../utils.ts"; import type { Document } from "../../mod.ts"; diff --git a/tests/serialized_collection/watchMany.test.ts b/tests/serialized_collection/watchMany.test.ts index d5c173d..0f895e8 100644 --- a/tests/serialized_collection/watchMany.test.ts +++ b/tests/serialized_collection/watchMany.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1, mockUser2, mockUser3 } from "../mocks.ts"; import { sleep, useDb } from "../utils.ts"; import type { Document } from "../../mod.ts"; diff --git a/tests/serialized_indexable_collection/add.test.ts b/tests/serialized_indexable_collection/add.test.ts index bc08b12..74977c3 100644 --- a/tests/serialized_indexable_collection/add.test.ts +++ b/tests/serialized_indexable_collection/add.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUserInvalid } from "../mocks.ts"; import { generateLargeUsers, useDb } from "../utils.ts"; diff --git a/tests/serialized_indexable_collection/addMany.test.ts b/tests/serialized_indexable_collection/addMany.test.ts index ee59972..adc4360 100644 --- a/tests/serialized_indexable_collection/addMany.test.ts +++ b/tests/serialized_indexable_collection/addMany.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { generateInvalidUsers, generateLargeUsers, useDb } from "../utils.ts"; const [user] = generateLargeUsers(1); diff --git a/tests/serialized_indexable_collection/count.test.ts b/tests/serialized_indexable_collection/count.test.ts index cc37f31..827e540 100644 --- a/tests/serialized_indexable_collection/count.test.ts +++ b/tests/serialized_indexable_collection/count.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { generateLargeUsers, useDb } from "../utils.ts"; Deno.test("serialized_indexable_collection - count", async (t) => { diff --git a/tests/serialized_indexable_collection/countBySecondaryIndex.test.ts b/tests/serialized_indexable_collection/countBySecondaryIndex.test.ts index ed967f0..9ba1bcf 100644 --- a/tests/serialized_indexable_collection/countBySecondaryIndex.test.ts +++ b/tests/serialized_indexable_collection/countBySecondaryIndex.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1, mockUser2, mockUser3 } from "../mocks.ts"; import { useDb } from "../utils.ts"; diff --git a/tests/serialized_indexable_collection/countBySecondaryOrder.test.ts b/tests/serialized_indexable_collection/countBySecondaryOrder.test.ts index faa5cd8..31b4cac 100644 --- a/tests/serialized_indexable_collection/countBySecondaryOrder.test.ts +++ b/tests/serialized_indexable_collection/countBySecondaryOrder.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1, mockUser2, mockUsersWithAlteredAge } from "../mocks.ts"; import { useDb } from "../utils.ts"; diff --git a/tests/serialized_indexable_collection/delete.test.ts b/tests/serialized_indexable_collection/delete.test.ts index 158d8e4..4d04e27 100644 --- a/tests/serialized_indexable_collection/delete.test.ts +++ b/tests/serialized_indexable_collection/delete.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { generateLargeUsers, useDb } from "../utils.ts"; const [user] = generateLargeUsers(1); diff --git a/tests/serialized_indexable_collection/deleteByPrimaryIndex.test.ts b/tests/serialized_indexable_collection/deleteByPrimaryIndex.test.ts index 5eecf03..8fcbbec 100644 --- a/tests/serialized_indexable_collection/deleteByPrimaryIndex.test.ts +++ b/tests/serialized_indexable_collection/deleteByPrimaryIndex.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { generateLargeUsers, useDb } from "../utils.ts"; const [user] = generateLargeUsers(1); diff --git a/tests/serialized_indexable_collection/deleteBySecondaryIndex.test.ts b/tests/serialized_indexable_collection/deleteBySecondaryIndex.test.ts index 690f4c4..8e67200 100644 --- a/tests/serialized_indexable_collection/deleteBySecondaryIndex.test.ts +++ b/tests/serialized_indexable_collection/deleteBySecondaryIndex.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { generateLargeUsers, useDb } from "../utils.ts"; const [user1, user2] = generateLargeUsers(2); diff --git a/tests/serialized_indexable_collection/deleteMany.test.ts b/tests/serialized_indexable_collection/deleteMany.test.ts index ba88203..0e45421 100644 --- a/tests/serialized_indexable_collection/deleteMany.test.ts +++ b/tests/serialized_indexable_collection/deleteMany.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { generateLargeUsers, useDb } from "../utils.ts"; Deno.test("serialized_indexable_collection - deleteMany", async (t) => { diff --git a/tests/serialized_indexable_collection/deleteManyBySecondaryOrder.test.ts b/tests/serialized_indexable_collection/deleteManyBySecondaryOrder.test.ts index af4b171..ad46078 100644 --- a/tests/serialized_indexable_collection/deleteManyBySecondaryOrder.test.ts +++ b/tests/serialized_indexable_collection/deleteManyBySecondaryOrder.test.ts @@ -1,4 +1,4 @@ -import { assert, assertEquals } from "../test.deps.ts"; +import { assert, assertEquals } from "@std/assert"; import { mockUser2, mockUsersWithAlteredAge } from "../mocks.ts"; import { useDb } from "../utils.ts"; diff --git a/tests/serialized_indexable_collection/enqueue.test.ts b/tests/serialized_indexable_collection/enqueue.test.ts index b6521e3..9dcf952 100644 --- a/tests/serialized_indexable_collection/enqueue.test.ts +++ b/tests/serialized_indexable_collection/enqueue.test.ts @@ -6,7 +6,7 @@ import { type QueueMessage, } from "../../mod.ts"; import { createHandlerId } from "../../src/utils.ts"; -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import type { User } from "../models.ts"; import { createResolver, useDb, useKv } from "../utils.ts"; @@ -17,8 +17,14 @@ Deno.test("serialized_indexable_collection - enqueue", async (t) => { const undeliveredId = "undelivered"; const sleeper = createResolver(); - const db = kvdex(kv, { - is_users: collection(model(), { indices: {}, serialized: true }), + const db = kvdex({ + kv, + schema: { + is_users: collection(model(), { + indices: {}, + serialized: true, + }), + }, }); const handlerId = createHandlerId(db.is_users._keys.base, undefined); diff --git a/tests/serialized_indexable_collection/find.test.ts b/tests/serialized_indexable_collection/find.test.ts index 9fff355..5535f96 100644 --- a/tests/serialized_indexable_collection/find.test.ts +++ b/tests/serialized_indexable_collection/find.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { generateLargeUsers, useDb } from "../utils.ts"; const [user] = generateLargeUsers(1); diff --git a/tests/serialized_indexable_collection/findByPrimaryIndex.test.ts b/tests/serialized_indexable_collection/findByPrimaryIndex.test.ts index 032b0fa..f05a64b 100644 --- a/tests/serialized_indexable_collection/findByPrimaryIndex.test.ts +++ b/tests/serialized_indexable_collection/findByPrimaryIndex.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { TransformUserModel } from "../models.ts"; import { generateLargeUsers, useDb } from "../utils.ts"; diff --git a/tests/serialized_indexable_collection/findBySecondaryIndex.test.ts b/tests/serialized_indexable_collection/findBySecondaryIndex.test.ts index ccf8469..2bec478 100644 --- a/tests/serialized_indexable_collection/findBySecondaryIndex.test.ts +++ b/tests/serialized_indexable_collection/findBySecondaryIndex.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1, mockUser2 } from "../mocks.ts"; import { TransformUserModel } from "../models.ts"; import { useDb } from "../utils.ts"; diff --git a/tests/serialized_indexable_collection/findMany.test.ts b/tests/serialized_indexable_collection/findMany.test.ts index d61922f..ac260c1 100644 --- a/tests/serialized_indexable_collection/findMany.test.ts +++ b/tests/serialized_indexable_collection/findMany.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { generateLargeUsers, useDb } from "../utils.ts"; Deno.test("serialized_indexable_collection - findMany", async (t) => { diff --git a/tests/serialized_indexable_collection/forEach.test.ts b/tests/serialized_indexable_collection/forEach.test.ts index 6d87a96..b41f874 100644 --- a/tests/serialized_indexable_collection/forEach.test.ts +++ b/tests/serialized_indexable_collection/forEach.test.ts @@ -1,5 +1,5 @@ import type { Document } from "../../mod.ts"; -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import type { User } from "../models.ts"; import { generateLargeUsers, useDb } from "../utils.ts"; diff --git a/tests/serialized_indexable_collection/forEachBySecondaryIndex.test.ts b/tests/serialized_indexable_collection/forEachBySecondaryIndex.test.ts index a265368..6c76041 100644 --- a/tests/serialized_indexable_collection/forEachBySecondaryIndex.test.ts +++ b/tests/serialized_indexable_collection/forEachBySecondaryIndex.test.ts @@ -1,5 +1,5 @@ import type { Document } from "../../mod.ts"; -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1, mockUser2, mockUser3 } from "../mocks.ts"; import type { User } from "../models.ts"; import { useDb } from "../utils.ts"; diff --git a/tests/serialized_indexable_collection/forEachBySecondaryOrder.test.ts b/tests/serialized_indexable_collection/forEachBySecondaryOrder.test.ts index 498104b..e50f9ce 100644 --- a/tests/serialized_indexable_collection/forEachBySecondaryOrder.test.ts +++ b/tests/serialized_indexable_collection/forEachBySecondaryOrder.test.ts @@ -1,5 +1,5 @@ import type { Document } from "../../mod.ts"; -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1, mockUser2, diff --git a/tests/serialized_indexable_collection/getMany.test.ts b/tests/serialized_indexable_collection/getMany.test.ts index c200421..286b13b 100644 --- a/tests/serialized_indexable_collection/getMany.test.ts +++ b/tests/serialized_indexable_collection/getMany.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { generateLargeUsers, useDb } from "../utils.ts"; Deno.test("serialized_indexable_collection - getMany", async (t) => { diff --git a/tests/serialized_indexable_collection/getManyBySecondaryOrder.test.ts b/tests/serialized_indexable_collection/getManyBySecondaryOrder.test.ts index a0b0608..02a105e 100644 --- a/tests/serialized_indexable_collection/getManyBySecondaryOrder.test.ts +++ b/tests/serialized_indexable_collection/getManyBySecondaryOrder.test.ts @@ -4,7 +4,7 @@ import { mockUser3, mockUsersWithAlteredAge, } from "../mocks.ts"; -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { useDb } from "../utils.ts"; Deno.test("serialized_indexable_collection - getManyBySecondaryOrder", async (t) => { diff --git a/tests/serialized_indexable_collection/getOne.test.ts b/tests/serialized_indexable_collection/getOne.test.ts index 0c9bfc0..78d1946 100644 --- a/tests/serialized_indexable_collection/getOne.test.ts +++ b/tests/serialized_indexable_collection/getOne.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { sleep, useDb } from "../utils.ts"; import { mockUser1, mockUser2 } from "../mocks.ts"; diff --git a/tests/serialized_indexable_collection/getOneBySecondaryIndex.test.ts b/tests/serialized_indexable_collection/getOneBySecondaryIndex.test.ts index ac1466b..2f993f3 100644 --- a/tests/serialized_indexable_collection/getOneBySecondaryIndex.test.ts +++ b/tests/serialized_indexable_collection/getOneBySecondaryIndex.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { sleep, useDb } from "../utils.ts"; import { mockUser1, mockUser2 } from "../mocks.ts"; diff --git a/tests/serialized_indexable_collection/getOneBySecondaryOrder.test.ts b/tests/serialized_indexable_collection/getOneBySecondaryOrder.test.ts index d590c6f..ab53c76 100644 --- a/tests/serialized_indexable_collection/getOneBySecondaryOrder.test.ts +++ b/tests/serialized_indexable_collection/getOneBySecondaryOrder.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { useDb } from "../utils.ts"; import { mockUser3, mockUsersWithAlteredAge } from "../mocks.ts"; diff --git a/tests/serialized_indexable_collection/history.test.ts b/tests/serialized_indexable_collection/history.test.ts index da547d3..26e1a0d 100644 --- a/tests/serialized_indexable_collection/history.test.ts +++ b/tests/serialized_indexable_collection/history.test.ts @@ -1,23 +1,27 @@ import { collection, kvdex, model } from "../../mod.ts"; -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { generateLargeUsers, sleep, useKv } from "../utils.ts"; import { mockUser1, mockUser2, mockUser3 } from "../mocks.ts"; import type { User } from "../models.ts"; +import { jsonEncoder } from "../../src/ext/encoding/mod.ts"; Deno.test("serialized_indexable_collection - history", async (t) => { await t.step( "Should persist history of multiple inserts in correct order", async () => { await useKv(async (kv) => { - const db = kvdex(kv, { - users: collection(model(), { - serialize: "json", - history: true, - indices: { - username: "primary", - age: "secondary", - }, - }), + const db = kvdex({ + kv, + schema: { + users: collection(model(), { + encoder: jsonEncoder(), + history: true, + indices: { + username: "primary", + age: "secondary", + }, + }), + }, }); const id = "id"; @@ -44,15 +48,18 @@ Deno.test("serialized_indexable_collection - history", async (t) => { "Should persist history of multiple inserts in correct order after deleting", async () => { await useKv(async (kv) => { - const db = kvdex(kv, { - users: collection(model(), { - serialize: "json", - history: true, - indices: { - username: "primary", - age: "secondary", - }, - }), + const db = kvdex({ + kv, + schema: { + users: collection(model(), { + encoder: jsonEncoder(), + history: true, + indices: { + username: "primary", + age: "secondary", + }, + }), + }, }); const id = "id"; @@ -87,15 +94,18 @@ Deno.test("serialized_indexable_collection - history", async (t) => { "Should persist history of multiple inserts and updates in correct order", async () => { await useKv(async (kv) => { - const db = kvdex(kv, { - users: collection(model(), { - serialize: "json", - history: true, - indices: { - username: "primary", - age: "secondary", - }, - }), + const db = kvdex({ + kv, + schema: { + users: collection(model(), { + encoder: jsonEncoder(), + history: true, + indices: { + username: "primary", + age: "secondary", + }, + }), + }, }); const id = "id"; @@ -122,15 +132,18 @@ Deno.test("serialized_indexable_collection - history", async (t) => { "Should persist version history of insert and delete by deleteMany()", async () => { await useKv(async (kv) => { - const db = kvdex(kv, { - users: collection(model(), { - serialize: "json", - history: true, - indices: { - username: "primary", - age: "secondary", - }, - }), + const db = kvdex({ + kv, + schema: { + users: collection(model(), { + encoder: jsonEncoder(), + history: true, + indices: { + username: "primary", + age: "secondary", + }, + }), + }, }); const id = "id"; @@ -160,14 +173,17 @@ Deno.test("serialized_indexable_collection - history", async (t) => { "Should not find history", async () => { await useKv(async (kv) => { - const db = kvdex(kv, { - users: collection(model(), { - serialize: "json", - indices: { - username: "primary", - age: "secondary", - }, - }), + const db = kvdex({ + kv, + schema: { + users: collection(model(), { + encoder: jsonEncoder(), + indices: { + username: "primary", + age: "secondary", + }, + }), + }, }); const id = "id"; @@ -184,15 +200,18 @@ Deno.test("serialized_indexable_collection - history", async (t) => { await t.step("Should find filtered history", async () => { await useKv(async (kv) => { - const db = kvdex(kv, { - users: collection(model(), { - history: true, - serialize: "json", - indices: { - username: "primary", - age: "secondary", - }, - }), + const db = kvdex({ + kv, + schema: { + users: collection(model(), { + history: true, + encoder: jsonEncoder(), + indices: { + username: "primary", + age: "secondary", + }, + }), + }, }); const id = "id"; @@ -229,15 +248,18 @@ Deno.test("serialized_indexable_collection - history", async (t) => { await t.step("Should delete all document history", async () => { await useKv(async (kv) => { - const db = kvdex(kv, { - users: collection(model(), { - serialize: "json", - history: true, - indices: { - username: "primary", - age: "secondary", - }, - }), + const db = kvdex({ + kv, + schema: { + users: collection(model(), { + encoder: jsonEncoder(), + history: true, + indices: { + username: "primary", + age: "secondary", + }, + }), + }, }); const id = "id"; diff --git a/tests/serialized_indexable_collection/listenQueue.test.ts b/tests/serialized_indexable_collection/listenQueue.test.ts index 8a571a4..5b57eda 100644 --- a/tests/serialized_indexable_collection/listenQueue.test.ts +++ b/tests/serialized_indexable_collection/listenQueue.test.ts @@ -10,9 +10,10 @@ import { UNDELIVERED_KEY_PREFIX, } from "../../src/constants.ts"; import { createHandlerId, extendKey } from "../../src/utils.ts"; -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import type { User } from "../models.ts"; import { createResolver, sleep, useKv } from "../utils.ts"; +import { jsonEncoder } from "../../src/ext/encoding/mod.ts"; Deno.test("serialized_indexable_collection - listenQueue", async (t) => { await t.step("Should receive message with string data", async () => { @@ -21,8 +22,14 @@ Deno.test("serialized_indexable_collection - listenQueue", async (t) => { const undeliveredId = "id"; const sleeper = createResolver(); - const db = kvdex(kv, { - is_users: collection(model(), { indices: {}, serialize: "json" }), + const db = kvdex({ + kv, + schema: { + is_users: collection(model(), { + indices: {}, + encoder: jsonEncoder(), + }), + }, }); const handlerId = createHandlerId(db.is_users._keys.base, undefined); @@ -61,8 +68,14 @@ Deno.test("serialized_indexable_collection - listenQueue", async (t) => { await t.step("Should not receive db queue message", async () => { await useKv(async (kv) => { - const db = kvdex(kv, { - is_users: collection(model(), { indices: {}, serialize: "json" }), + const db = kvdex({ + kv, + schema: { + is_users: collection(model(), { + indices: {}, + encoder: jsonEncoder(), + }), + }, }); let assertion = true; diff --git a/tests/serialized_indexable_collection/map.test.ts b/tests/serialized_indexable_collection/map.test.ts index 46ee615..8d7a740 100644 --- a/tests/serialized_indexable_collection/map.test.ts +++ b/tests/serialized_indexable_collection/map.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { generateLargeUsers, useDb } from "../utils.ts"; Deno.test("serialized_indexable_collection - map", async (t) => { diff --git a/tests/serialized_indexable_collection/mapBySecondaryIndex.test.ts b/tests/serialized_indexable_collection/mapBySecondaryIndex.test.ts index 0384ae2..c1c2e72 100644 --- a/tests/serialized_indexable_collection/mapBySecondaryIndex.test.ts +++ b/tests/serialized_indexable_collection/mapBySecondaryIndex.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1, mockUser2, mockUser3 } from "../mocks.ts"; import { useDb } from "../utils.ts"; diff --git a/tests/serialized_indexable_collection/mapBySecondaryOrder.test.ts b/tests/serialized_indexable_collection/mapBySecondaryOrder.test.ts index 5d38eac..e0aac13 100644 --- a/tests/serialized_indexable_collection/mapBySecondaryOrder.test.ts +++ b/tests/serialized_indexable_collection/mapBySecondaryOrder.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1, mockUser2, diff --git a/tests/serialized_indexable_collection/properties.test.ts b/tests/serialized_indexable_collection/properties.test.ts index bd89c94..ac580c8 100644 --- a/tests/serialized_indexable_collection/properties.test.ts +++ b/tests/serialized_indexable_collection/properties.test.ts @@ -12,13 +12,14 @@ import { SECONDARY_INDEX_KEY_PREFIX, } from "../../src/constants.ts"; import { extendKey, keyEq } from "../../src/utils.ts"; -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import type { User } from "../models.ts"; import { generateLargeUsers, useDb, useKv } from "../utils.ts"; import { mockUser1 } from "../mocks.ts"; import { mockUser2 } from "../mocks.ts"; import { mockUser3 } from "../mocks.ts"; import { sleep } from "../utils.ts"; +import { jsonEncoder } from "../../src/ext/encoding/mod.ts"; const [user] = generateLargeUsers(1); @@ -44,17 +45,20 @@ Deno.test("serialized_indexable_collection - properties", async (t) => { await t.step("Should generate ids with custom id generator", async () => { await useKv((kv) => { - const db = kvdex(kv, { - users1: collection(model(), { - idGenerator: () => Math.random(), - indices: {}, - serialize: "json", - }), - users2: collection(model(), { - idGenerator: (data) => data.username, - indices: {}, - serialize: "json", - }), + const db = kvdex({ + kv, + schema: { + users1: collection(model(), { + idGenerator: () => Math.random(), + indices: {}, + encoder: jsonEncoder(), + }), + users2: collection(model(), { + idGenerator: (data) => data.username, + indices: {}, + encoder: jsonEncoder(), + }), + }, }); const id1 = db.users1._idGenerator(user); @@ -237,25 +241,28 @@ Deno.test("serialized_indexable_collection - properties", async (t) => { await t.step("Should allow optional indices", async () => { await useKv(async (kv) => { - const db = kvdex(kv, { - is: collection( - model<{ - oblPrimary: string; - oblSecondary: number; - optPrimary?: string; - optSecondary?: number; - check?: Date; - }>(), - { - indices: { - oblPrimary: "primary", - oblSecondary: "secondary", - optPrimary: "primary", - optSecondary: "secondary", + const db = kvdex({ + kv, + schema: { + is: collection( + model<{ + oblPrimary: string; + oblSecondary: number; + optPrimary?: string; + optSecondary?: number; + check?: Date; + }>(), + { + indices: { + oblPrimary: "primary", + oblSecondary: "secondary", + optPrimary: "primary", + optSecondary: "secondary", + }, + encoder: jsonEncoder(), }, - serialize: "json", - }, - ), + ), + }, }); const cr1 = await db.is.add({ @@ -465,7 +472,6 @@ Deno.test("serialized_indexable_collection - properties", async (t) => { val20, val21, val22, - val23, }; const val25 = new Set(val23); const val26 = new Map([ @@ -493,189 +499,192 @@ Deno.test("serialized_indexable_collection - properties", async (t) => { ["val22", val22], ]); - const db = kvdex(kv, { - val1: collection(model(), { - serialize: "json", - indices: { - p: "primary", - s: "secondary", - }, - }), - val2: collection(model(), { - serialize: "json", - indices: { - p: "primary", - s: "secondary", - }, - }), - val3: collection(model(), { - serialize: "json", - indices: { - p: "primary", - s: "secondary", - }, - }), - val4: collection(model(), { - serialize: "json", - indices: { - p: "primary", - s: "secondary", - }, - }), - val5: collection(model(), { - serialize: "json", - indices: { - p: "primary", - s: "secondary", - }, - }), - val6: collection(model(), { - serialize: "json", - indices: { - p: "primary", - s: "secondary", - }, - }), - val7: collection(model(), { - serialize: "json", - indices: { - p: "primary", - s: "secondary", - }, - }), - val8: collection(model(), { - serialize: "json", - indices: { - p: "primary", - s: "secondary", - }, - }), - val9: collection(model(), { - serialize: "json", - indices: { - p: "primary", - s: "secondary", - }, - }), - val10: collection(model(), { - serialize: "json", - indices: { - p: "primary", - s: "secondary", - }, - }), - val11: collection(model(), { - serialize: "json", - indices: { - p: "primary", - s: "secondary", - }, - }), - val12: collection(model(), { - serialize: "json", - indices: { - p: "primary", - s: "secondary", - }, - }), - val13: collection(model(), { - serialize: "json", - indices: { - p: "primary", - s: "secondary", - }, - }), - val14: collection(model(), { - serialize: "json", - indices: { - p: "primary", - s: "secondary", - }, - }), - val15: collection(model(), { - serialize: "json", - indices: { - p: "primary", - s: "secondary", - }, - }), - val16: collection(model(), { - serialize: "json", - indices: { - p: "primary", - s: "secondary", - }, - }), - val17: collection(model(), { - serialize: "json", - indices: { - p: "primary", - s: "secondary", - }, - }), - val18: collection(model(), { - serialize: "json", - indices: { - p: "primary", - s: "secondary", - }, - }), - val19: collection(model(), { - serialize: "json", - indices: { - p: "primary", - s: "secondary", - }, - }), - val20: collection(model(), { - serialize: "json", - indices: { - p: "primary", - s: "secondary", - }, - }), - val21: collection(model(), { - serialize: "json", - indices: { - p: "primary", - s: "secondary", - }, - }), - val22: collection(model(), { - serialize: "json", - indices: { - p: "primary", - s: "secondary", - }, - }), - val23: collection(model(), { - serialize: "json", - indices: { - p: "primary", - s: "secondary", - }, - }), - val24: collection(model(), { - serialize: "json", - indices: { - p: "primary", - s: "secondary", - }, - }), - val25: collection(model(), { - serialize: "json", - indices: { - p: "primary", - s: "secondary", - }, - }), - val26: collection(model(), { - serialize: "json", - indices: { - p: "primary", - s: "secondary", - }, - }), + const db = kvdex({ + kv, + schema: { + val1: collection(model(), { + encoder: jsonEncoder(), + indices: { + p: "primary", + s: "secondary", + }, + }), + val2: collection(model(), { + encoder: jsonEncoder(), + indices: { + p: "primary", + s: "secondary", + }, + }), + val3: collection(model(), { + encoder: jsonEncoder(), + indices: { + p: "primary", + s: "secondary", + }, + }), + val4: collection(model(), { + encoder: jsonEncoder(), + indices: { + p: "primary", + s: "secondary", + }, + }), + val5: collection(model(), { + encoder: jsonEncoder(), + indices: { + p: "primary", + s: "secondary", + }, + }), + val6: collection(model(), { + encoder: jsonEncoder(), + indices: { + p: "primary", + s: "secondary", + }, + }), + val7: collection(model(), { + encoder: jsonEncoder(), + indices: { + p: "primary", + s: "secondary", + }, + }), + val8: collection(model(), { + encoder: jsonEncoder(), + indices: { + p: "primary", + s: "secondary", + }, + }), + val9: collection(model(), { + encoder: jsonEncoder(), + indices: { + p: "primary", + s: "secondary", + }, + }), + val10: collection(model(), { + encoder: jsonEncoder(), + indices: { + p: "primary", + s: "secondary", + }, + }), + val11: collection(model(), { + encoder: jsonEncoder(), + indices: { + p: "primary", + s: "secondary", + }, + }), + val12: collection(model(), { + encoder: jsonEncoder(), + indices: { + p: "primary", + s: "secondary", + }, + }), + val13: collection(model(), { + encoder: jsonEncoder(), + indices: { + p: "primary", + s: "secondary", + }, + }), + val14: collection(model(), { + encoder: jsonEncoder(), + indices: { + p: "primary", + s: "secondary", + }, + }), + val15: collection(model(), { + encoder: jsonEncoder(), + indices: { + p: "primary", + s: "secondary", + }, + }), + val16: collection(model(), { + encoder: jsonEncoder(), + indices: { + p: "primary", + s: "secondary", + }, + }), + val17: collection(model(), { + encoder: jsonEncoder(), + indices: { + p: "primary", + s: "secondary", + }, + }), + val18: collection(model(), { + encoder: jsonEncoder(), + indices: { + p: "primary", + s: "secondary", + }, + }), + val19: collection(model(), { + encoder: jsonEncoder(), + indices: { + p: "primary", + s: "secondary", + }, + }), + val20: collection(model(), { + encoder: jsonEncoder(), + indices: { + p: "primary", + s: "secondary", + }, + }), + val21: collection(model(), { + encoder: jsonEncoder(), + indices: { + p: "primary", + s: "secondary", + }, + }), + val22: collection(model(), { + encoder: jsonEncoder(), + indices: { + p: "primary", + s: "secondary", + }, + }), + val23: collection(model(), { + encoder: jsonEncoder(), + indices: { + p: "primary", + s: "secondary", + }, + }), + val24: collection(model(), { + encoder: jsonEncoder(), + indices: { + p: "primary", + s: "secondary", + }, + }), + val25: collection(model(), { + encoder: jsonEncoder(), + indices: { + p: "primary", + s: "secondary", + }, + }), + val26: collection(model(), { + encoder: jsonEncoder(), + indices: { + p: "primary", + s: "secondary", + }, + }), + }, }); const cr1 = await db.val1.add({ p: val1, s: val1 }); @@ -923,21 +932,24 @@ Deno.test("serialized_indexable_collection - properties", async (t) => { await t.step("Should successfully generate id asynchronously", async () => { await useKv(async (kv) => { - const db = kvdex(kv, { - test: collection(model(), { - serialize: "json", - indices: { - username: "primary", - age: "secondary", - }, - idGenerator: async (user) => { - const buffer = await crypto.subtle.digest( - "SHA-256", - new ArrayBuffer(user.age), - ); - return Math.random() * buffer.byteLength; - }, - }), + const db = kvdex({ + kv, + schema: { + test: collection(model(), { + encoder: jsonEncoder(), + indices: { + username: "primary", + age: "secondary", + }, + idGenerator: async (user) => { + const buffer = await crypto.subtle.digest( + "SHA-256", + new ArrayBuffer(user.age), + ); + return Math.random() * buffer.byteLength; + }, + }), + }, }); const cr1 = await db.test.add(mockUser1); diff --git a/tests/serialized_indexable_collection/set.test.ts b/tests/serialized_indexable_collection/set.test.ts index b2133a5..d97d77a 100644 --- a/tests/serialized_indexable_collection/set.test.ts +++ b/tests/serialized_indexable_collection/set.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUserInvalid } from "../mocks.ts"; import { generateLargeUsers, useDb } from "../utils.ts"; diff --git a/tests/serialized_indexable_collection/types.test.ts b/tests/serialized_indexable_collection/types.test.ts index ccfb9ae..bff01c4 100644 --- a/tests/serialized_indexable_collection/types.test.ts +++ b/tests/serialized_indexable_collection/types.test.ts @@ -1,5 +1,6 @@ import { collection, kvdex, model } from "../../mod.ts"; -import { assert, assertEquals } from "../test.deps.ts"; +import { jsonEncoder } from "../../src/ext/encoding/mod.ts"; +import { assert, assertEquals } from "@std/assert"; import { useKv } from "../utils.ts"; import { TObject } from "../values.ts"; @@ -8,14 +9,17 @@ Deno.test("serialized_indexable_collection - types", async (t) => { "Should allow and properly store/retrieve all KvValue types", async () => { await useKv(async (kv) => { - const db = kvdex(kv, { - objects: collection(model(), { - serialize: "v8", - indices: { - TString: "primary", - TNumber: "secondary", - }, - }), + const db = kvdex({ + kv, + schema: { + objects: collection(model(), { + encoder: jsonEncoder(), + indices: { + TString: "primary", + TNumber: "secondary", + }, + }), + }, }); const cr = await db.objects.add(TObject); diff --git a/tests/serialized_indexable_collection/update.test.ts b/tests/serialized_indexable_collection/update.test.ts index ad8ad71..deaefd2 100644 --- a/tests/serialized_indexable_collection/update.test.ts +++ b/tests/serialized_indexable_collection/update.test.ts @@ -1,5 +1,5 @@ import type { Document } from "../../mod.ts"; -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1, mockUser2, mockUser3, mockUserInvalid } from "../mocks.ts"; import type { User } from "../models.ts"; import { useDb } from "../utils.ts"; diff --git a/tests/serialized_indexable_collection/updateByPrimaryIndex.test.ts b/tests/serialized_indexable_collection/updateByPrimaryIndex.test.ts index 4596ee2..315b12a 100644 --- a/tests/serialized_indexable_collection/updateByPrimaryIndex.test.ts +++ b/tests/serialized_indexable_collection/updateByPrimaryIndex.test.ts @@ -1,5 +1,5 @@ import type { Document } from "../../mod.ts"; -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1, mockUser2, mockUserInvalid } from "../mocks.ts"; import type { User } from "../models.ts"; import { useDb } from "../utils.ts"; diff --git a/tests/serialized_indexable_collection/updateBySecondaryIndex.test.ts b/tests/serialized_indexable_collection/updateBySecondaryIndex.test.ts index f8c1fdd..aa158b7 100644 --- a/tests/serialized_indexable_collection/updateBySecondaryIndex.test.ts +++ b/tests/serialized_indexable_collection/updateBySecondaryIndex.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1, mockUserInvalid } from "../mocks.ts"; import { generateUsers, useDb } from "../utils.ts"; diff --git a/tests/serialized_indexable_collection/updateMany.test.ts b/tests/serialized_indexable_collection/updateMany.test.ts index 190b2e4..608ef49 100644 --- a/tests/serialized_indexable_collection/updateMany.test.ts +++ b/tests/serialized_indexable_collection/updateMany.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1, mockUserInvalid } from "../mocks.ts"; import { generateUsers, useDb } from "../utils.ts"; diff --git a/tests/serialized_indexable_collection/updateManyBySecondaryOrder.test.ts b/tests/serialized_indexable_collection/updateManyBySecondaryOrder.test.ts index 5e4cfd9..2285d4c 100644 --- a/tests/serialized_indexable_collection/updateManyBySecondaryOrder.test.ts +++ b/tests/serialized_indexable_collection/updateManyBySecondaryOrder.test.ts @@ -1,4 +1,4 @@ -import { assert, assertEquals } from "../test.deps.ts"; +import { assert, assertEquals } from "@std/assert"; import { mockUser1, mockUser2, @@ -8,6 +8,7 @@ import { import { generateUsers, useDb } from "../utils.ts"; import type { User } from "../models.ts"; +// TODO: fix update document deleting indices even when failing to update document Deno.test.ignore( "serialized_indexable_collection - updateManyBySecondaryOrder", async (t) => { diff --git a/tests/serialized_indexable_collection/updateOne.test.ts b/tests/serialized_indexable_collection/updateOne.test.ts index 131df89..e6b0c3e 100644 --- a/tests/serialized_indexable_collection/updateOne.test.ts +++ b/tests/serialized_indexable_collection/updateOne.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1, mockUser2, mockUser3, mockUserInvalid } from "../mocks.ts"; import { sleep, useDb } from "../utils.ts"; diff --git a/tests/serialized_indexable_collection/updateOneBySecondaryIndex.test.ts b/tests/serialized_indexable_collection/updateOneBySecondaryIndex.test.ts index 0c5f0f0..1e04864 100644 --- a/tests/serialized_indexable_collection/updateOneBySecondaryIndex.test.ts +++ b/tests/serialized_indexable_collection/updateOneBySecondaryIndex.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1, mockUser2, mockUser3, mockUserInvalid } from "../mocks.ts"; import { sleep, useDb } from "../utils.ts"; diff --git a/tests/serialized_indexable_collection/updateOneBySecondaryOrder.test.ts b/tests/serialized_indexable_collection/updateOneBySecondaryOrder.test.ts index 0f96416..2039a4e 100644 --- a/tests/serialized_indexable_collection/updateOneBySecondaryOrder.test.ts +++ b/tests/serialized_indexable_collection/updateOneBySecondaryOrder.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1, mockUser2, diff --git a/tests/serialized_indexable_collection/upsert.test.ts b/tests/serialized_indexable_collection/upsert.test.ts index 9545688..b3023d2 100644 --- a/tests/serialized_indexable_collection/upsert.test.ts +++ b/tests/serialized_indexable_collection/upsert.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1, mockUser2, mockUser3 } from "../mocks.ts"; import type { User } from "../models.ts"; import { useDb } from "../utils.ts"; diff --git a/tests/serialized_indexable_collection/upsertByPrimaryIndex.test.ts b/tests/serialized_indexable_collection/upsertByPrimaryIndex.test.ts index 688b2c2..0a01d86 100644 --- a/tests/serialized_indexable_collection/upsertByPrimaryIndex.test.ts +++ b/tests/serialized_indexable_collection/upsertByPrimaryIndex.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1, mockUser2, mockUser3 } from "../mocks.ts"; import type { User } from "../models.ts"; import { useDb } from "../utils.ts"; diff --git a/tests/serialized_indexable_collection/watch.test.ts b/tests/serialized_indexable_collection/watch.test.ts index 0c2782b..43152c9 100644 --- a/tests/serialized_indexable_collection/watch.test.ts +++ b/tests/serialized_indexable_collection/watch.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1, mockUser2, mockUser3 } from "../mocks.ts"; import { sleep, useDb } from "../utils.ts"; import type { Document } from "../../mod.ts"; diff --git a/tests/serialized_indexable_collection/watchMany.test.ts b/tests/serialized_indexable_collection/watchMany.test.ts index 9d79c1c..1274bbb 100644 --- a/tests/serialized_indexable_collection/watchMany.test.ts +++ b/tests/serialized_indexable_collection/watchMany.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { mockUser1, mockUser2, mockUser3 } from "../mocks.ts"; import { generateUsers, sleep, useDb } from "../utils.ts"; import type { Document } from "../../mod.ts"; diff --git a/tests/test.deps.ts b/tests/test.deps.ts deleted file mode 100644 index c4046b2..0000000 --- a/tests/test.deps.ts +++ /dev/null @@ -1,6 +0,0 @@ -export { assert } from "jsr:@std/assert@^0.217/assert"; -export { assertEquals } from "jsr:@std/assert@^0.217/assert_equals"; -export { assertNotEquals } from "jsr:@std/assert@^0.217/assert_not_equals"; -export { assertThrows } from "jsr:@std/assert@^0.217/assert_throws"; -export { z } from "npm:zod@^3.22"; -export type { Kv } from "npm:@deno/kv"; diff --git a/tests/utils.ts b/tests/utils.ts index 79c9de6..be89dfd 100644 --- a/tests/utils.ts +++ b/tests/utils.ts @@ -1,66 +1,75 @@ import { collection, type DenoKv, type DenoKvU64, kvdex } from "../mod.ts"; +import { brotliCompressor } from "../src/ext/encoding/brotli/brotli_compressor.ts"; +import { jsonEncoder } from "../src/ext/encoding/mod.ts"; import { MapKv } from "../src/ext/kv/map_kv.ts"; import { model } from "../src/model.ts"; import { TransformUserModel, type User, UserSchema } from "./models.ts"; +export const testEncoder = jsonEncoder({ + compressor: brotliCompressor(), +}); + // Create test db export function createDb(kv: DenoKv) { - return kvdex(kv, { - u64s: collection(model()), - s_u64s: collection(model(), { - serialize: "json", - }), - users: collection(model()), - i_users: collection(model(), { - indices: { - username: "primary", - age: "secondary", - }, - }), - s_users: collection(model(), { - serialize: "json", - }), - is_users: collection(model(), { - indices: { - username: "primary", - age: "secondary", - }, - serialize: "json", - }), - z_users: collection(UserSchema), - zi_users: collection(UserSchema, { - indices: { - username: "primary", - age: "secondary", - }, - }), - zs_users: collection(UserSchema, { - serialize: "json", - }), - zis_users: collection(UserSchema, { - indices: { - username: "primary", - age: "secondary", - }, - serialize: "json", - }), - a_users: collection(TransformUserModel), - ai_users: collection(TransformUserModel, { - indices: { - name: "primary", - decadeAge: "secondary", - }, - }), - as_users: collection(TransformUserModel, { - serialize: "json", - }), - ais_users: collection(TransformUserModel, { - indices: { - name: "primary", - decadeAge: "secondary", - }, - serialize: "json", - }), + return kvdex({ + kv, + schema: { + u64s: collection(model()), + s_u64s: collection(model(), { + encoder: testEncoder, + }), + users: collection(model()), + i_users: collection(model(), { + indices: { + username: "primary", + age: "secondary", + }, + }), + s_users: collection(model(), { + encoder: testEncoder, + }), + is_users: collection(model(), { + indices: { + username: "primary", + age: "secondary", + }, + encoder: testEncoder, + }), + z_users: collection(UserSchema), + zi_users: collection(UserSchema, { + indices: { + username: "primary", + age: "secondary", + }, + }), + zs_users: collection(UserSchema, { + encoder: testEncoder, + }), + zis_users: collection(UserSchema, { + indices: { + username: "primary", + age: "secondary", + }, + encoder: testEncoder, + }), + a_users: collection(TransformUserModel), + ai_users: collection(TransformUserModel, { + indices: { + name: "primary", + decadeAge: "secondary", + }, + }), + as_users: collection(TransformUserModel, { + encoder: testEncoder, + }), + ais_users: collection(TransformUserModel, { + indices: { + name: "primary", + decadeAge: "secondary", + }, + encoder: testEncoder, + }), + }, }); } diff --git a/tests/utils/isKvObject.test.ts b/tests/utils/isKvObject.test.ts index 196d25c..bfdf353 100644 --- a/tests/utils/isKvObject.test.ts +++ b/tests/utils/isKvObject.test.ts @@ -1,4 +1,4 @@ -import { assert } from "../test.deps.ts"; +import { assert } from "@std/assert"; import { TKvU64, TObject, VALUES } from "../values.ts"; import { isKvObject } from "../../src/utils.ts"; diff --git a/tests/utils/jsonDeserialize.test.ts b/tests/utils/jsonDeserialize.test.ts deleted file mode 100644 index ba75509..0000000 --- a/tests/utils/jsonDeserialize.test.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { jsonDeserialize, jsonSerialize } from "../../src/utils.ts"; -import { assertEquals } from "../test.deps.ts"; -import { VALUES } from "../values.ts"; - -Deno.test("utils - jsonDeserialize", async (t) => { - await t.step( - "Should successfully deserialize all KvValue type values from Uint8Array", - () => { - const serialized = VALUES.map(jsonSerialize); - const deserialized = serialized.map(jsonDeserialize); - assertEquals(VALUES, deserialized); - }, - ); -}); diff --git a/tests/utils/jsonSerialize.test.ts b/tests/utils/jsonSerialize.test.ts deleted file mode 100644 index adf3f6e..0000000 --- a/tests/utils/jsonSerialize.test.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { jsonSerialize } from "../../src/utils.ts"; -import { assert } from "../test.deps.ts"; -import { VALUES } from "../values.ts"; - -Deno.test("utils - jsonSerialize", async (t) => { - await t.step( - "Should successfully serialize all KvValue type values", - () => { - const serialized = VALUES.map(jsonSerialize); - assert(serialized.every((val) => val instanceof Uint8Array)); - }, - ); -}); diff --git a/tests/utils/v8Deserialize.test.ts b/tests/utils/v8Deserialize.test.ts deleted file mode 100644 index 665f42b..0000000 --- a/tests/utils/v8Deserialize.test.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { v8Deserialize, v8Serialize } from "../../src/utils.ts"; -import { assertEquals } from "../test.deps.ts"; -import { VALUES } from "../values.ts"; - -Deno.test("utils - v8Deserialize", async (t) => { - await t.step( - "Should successfully deserialize all KvValue type values from Uint8Array", - () => { - const serialized = VALUES.map(v8Serialize); - const deserialized = serialized.map((val) => { - try { - return v8Deserialize(val); - } catch (e) { - throw new Error(`Failed to deserialize value: ${val}, Error: ${e}`); - } - }); - assertEquals(VALUES, deserialized); - }, - ); -}); diff --git a/tests/utils/v8Serialize.test.ts b/tests/utils/v8Serialize.test.ts deleted file mode 100644 index c063fda..0000000 --- a/tests/utils/v8Serialize.test.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { v8Serialize } from "../../src/utils.ts"; -import { assert } from "../test.deps.ts"; -import { VALUES } from "../values.ts"; - -Deno.test("utils - v8Serialize", async (t) => { - await t.step( - "Should successfully serialize all KvValue type values", - () => { - const serialized = VALUES.map((val) => { - try { - return v8Serialize(val); - } catch (e) { - throw new Error( - `Failed to serialize value: ${val}, Error: ${e}`, - ); - } - }); - assert(serialized.every((val) => val instanceof Uint8Array)); - }, - ); -}); diff --git a/tests/values.ts b/tests/values.ts index 6d46834..a2ad918 100644 --- a/tests/values.ts +++ b/tests/values.ts @@ -18,7 +18,7 @@ export const TUint16Array = new Uint16Array([10, 20, 30]); export const TUint32Array = new Uint32Array([10, 20, 30]); export const TBigUint64Array = new BigUint64Array([10n, 20n, 30n]); export const TUint8ClampedArray = new Uint8ClampedArray([10, 20, 30]); -export const TFloat16Array = new Float16Array([10.203423878293472837429384]); +// TODO: export const TFloat16Array = new Float16Array([10.203423878293472837429384]); export const TFloat32Array = new Float32Array([10.203423878293472837429384]); export const TFloat64Array = new Float64Array([10.203423878293472837429384]); export const TBuffer = new Uint8Array([10, 20, 30]).buffer; @@ -45,7 +45,7 @@ export const TArray = [ TUint32Array, TBigUint64Array, TUint8ClampedArray, - TFloat16Array, + //TFloat16Array, TFloat32Array, TFloat64Array, TBuffer, @@ -73,7 +73,7 @@ export const TObject = { TUint32Array, TBigUint64Array, TUint8ClampedArray, - TFloat16Array, + // TODO: TFloat16Array, TFloat32Array, TFloat64Array, TBuffer, @@ -106,7 +106,7 @@ export const VALUES = [ TUint32Array, TBigUint64Array, TUint8ClampedArray, - TFloat16Array, + // TODO: TFloat16Array, TFloat32Array, TFloat64Array, TBuffer,