From 1969385765bc07f209dc4b8850d6d13358af06a6 Mon Sep 17 00:00:00 2001 From: Oliver O'Loughlin Date: Fri, 7 Feb 2025 18:15:46 +0100 Subject: [PATCH 1/4] refactor: move internals to dedicated object --- src/atomic_builder.ts | 51 +-- src/collection.ts | 291 +++++++++++------- src/utils.ts | 16 +- tests/collection/add.test.ts | 1 - tests/collection/enqueue.test.ts | 5 +- tests/collection/listenQueue.test.ts | 5 +- tests/collection/properties.test.ts | 8 +- tests/db/atomic.test.ts | 5 +- tests/db/kvdex.test.ts | 4 +- tests/indexable_collection/enqueue.test.ts | 5 +- .../indexable_collection/listenQueue.test.ts | 5 +- tests/indexable_collection/properties.test.ts | 12 +- tests/serialized_collection/enqueue.test.ts | 5 +- .../serialized_collection/listenQueue.test.ts | 5 +- .../serialized_collection/properties.test.ts | 10 +- .../enqueue.test.ts | 5 +- .../listenQueue.test.ts | 5 +- .../properties.test.ts | 12 +- 18 files changed, 271 insertions(+), 179 deletions(-) diff --git a/src/atomic_builder.ts b/src/atomic_builder.ts index 4f08818..cd0126f 100644 --- a/src/atomic_builder.ts +++ b/src/atomic_builder.ts @@ -71,7 +71,7 @@ export class AtomicBuilder< operations?: Operations, ) { // Check for large collection - if (collection._encoder) { + if (collection.一internal.encoder) { throw new InvalidCollectionError( "Atomic operations are not supported for serialized collections", ); @@ -187,15 +187,17 @@ export class AtomicBuilder< delete(id: ParseId): this { // Create id key from id and collection id key const collection = this.collection; - const idKey = extendKey(collection._keys.id, id); + const idKey = extendKey(collection.一internal.keys.id, id); // Add delete operation this.operations.atomic.delete(idKey); // If collection is indexable, handle indexing - if (this.collection._isIndexable) { + if (this.collection.一internal.isIndexable) { // Add collection key for collision detection - this.operations.indexDeleteCollectionKeys.push(collection._keys.base); + this.operations.indexDeleteCollectionKeys.push( + collection.一internal.keys.base, + ); // Add delete preperation function to prepeare delete functions list this.operations.prepareDeleteFns.push(async (kv) => { @@ -209,8 +211,12 @@ export class AtomicBuilder< } // Set history entry if keeps history - if (this.collection._keepsHistory) { - const historyKey = extendKey(this.collection._keys.history, id, ulid()); + if (this.collection.一internal.keepsHistory) { + const historyKey = extendKey( + this.collection.一internal.keys.history, + id, + ulid(), + ); const historyEntry: HistoryEntry = { type: "delete", @@ -244,7 +250,7 @@ export class AtomicBuilder< // Create Denoatomic checks from atomci checks input list const checks: DenoAtomicCheck[] = atomicChecks.map( ({ id, versionstamp }) => { - const key = extendKey(this.collection._keys.id, id); + const key = extendKey(this.collection.一internal.keys.id, id); return { key, versionstamp, @@ -278,7 +284,7 @@ export class AtomicBuilder< id: ParseId, value: TOutput extends DenoKvU64 ? bigint : never, ): this { - const idKey = extendKey(this.collection._keys.id, id); + const idKey = extendKey(this.collection.一internal.keys.id, id); this.operations.atomic.sum(idKey, value); return this; } @@ -303,7 +309,7 @@ export class AtomicBuilder< id: ParseId, value: TOutput extends DenoKvU64 ? bigint : never, ): this { - const idKey = extendKey(this.collection._keys.id, id); + const idKey = extendKey(this.collection.一internal.keys.id, id); this.operations.atomic.min(idKey, value); return this; } @@ -328,7 +334,7 @@ export class AtomicBuilder< id: ParseId, value: TOutput extends DenoKvU64 ? bigint : never, ): this { - const idKey = extendKey(this.collection._keys.id, id); + const idKey = extendKey(this.collection.一internal.keys.id, id); this.operations.atomic.max(idKey, value); return this; } @@ -418,8 +424,8 @@ export class AtomicBuilder< enqueue(data: KvValue, options?: EnqueueOptions): this { // Prepare and add enqueue operation const prep = prepareEnqueue( - this.collection._keys.base, - this.collection._keys.undelivered, + this.collection.一internal.keys.base, + this.collection.一internal.keys.undelivered, data, options, ); @@ -517,7 +523,7 @@ export class AtomicBuilder< const overwrite = !!(options as AtomicSetOptions | undefined) ?.overwrite; - if (this.collection._isIndexable && overwrite) { + if (this.collection.一internal.isIndexable && overwrite) { throw new InvalidCollectionError( "The overwrite property is not supported for indexable collections", ); @@ -527,11 +533,12 @@ export class AtomicBuilder< // Create id key from collection id key and id const collection = this.collection; - const parsed = collection._model._transform?.(value as TInput) ?? - collection._model.parse(value); + const parsed = + collection.一internal.model._transform?.(value as TInput) ?? + collection.一internal.model.parse(value); - const docId = id ?? await collection._idGenerator(parsed); - const idKey = extendKey(collection._keys.id, docId); + const docId = id ?? await collection.一internal.idGenerator(parsed); + const idKey = extendKey(collection.一internal.keys.id, docId); // Add set operation this.operations.atomic.set(idKey, parsed, options); @@ -539,9 +546,11 @@ export class AtomicBuilder< this.operations.atomic.check({ key: idKey, versionstamp: null }); } - if (collection._isIndexable) { + if (collection.一internal.isIndexable) { // Add collection id key for collision detection - this.operations.indexAddCollectionKeys.push(collection._keys.base); + this.operations.indexAddCollectionKeys.push( + collection.一internal.keys.base, + ); // Add indexing operations await setIndices( @@ -555,9 +564,9 @@ export class AtomicBuilder< } // Set history entry if keeps history - if (this.collection._keepsHistory) { + if (this.collection.一internal.keepsHistory) { const historyKey = extendKey( - this.collection._keys.history, + this.collection.一internal.keys.history, docId, ulid(), ); diff --git a/src/collection.ts b/src/collection.ts index 85cf669..dfa0089 100644 --- a/src/collection.ts +++ b/src/collection.ts @@ -154,14 +154,17 @@ export class Collection< private queueHandlers: QueueHandlers; private idempotentListener: IdempotentListener; - readonly _model: Model; - readonly _primaryIndexList: string[]; - readonly _secondaryIndexList: string[]; - readonly _keys: CollectionKeys; - readonly _idGenerator: IdGenerator>; - readonly _encoder?: Encoder; - readonly _isIndexable: boolean; - readonly _keepsHistory: boolean; + /** Used for internal workings, do not manipulate or rely on these properties. */ + readonly 一internal: { + readonly model: Model; + readonly primaryIndexList: string[]; + readonly secondaryIndexList: string[]; + readonly keys: CollectionKeys; + readonly idGenerator: IdGenerator>; + readonly encoder?: Encoder; + readonly isIndexable: boolean; + readonly keepsHistory: boolean; + }; constructor( kv: DenoKv, @@ -175,12 +178,9 @@ export class Collection< this.kv = kv; this.queueHandlers = queueHandlers; this.idempotentListener = idempotentListener; - this._model = model; - this._idGenerator = options?.idGenerator ?? generateId as any; - this._encoder = options?.encoder; // Set keys - this._keys = { + const keys = { base: extendKey([KVDEX_KEY_PREFIX], ...key), id: extendKey( [KVDEX_KEY_PREFIX], @@ -224,23 +224,31 @@ export class Collection< const opts = (options ?? {}) as PossibleCollectionOptions; // Set index lists - this._primaryIndexList = []; - this._secondaryIndexList = []; + const primaryIndexList: string[] = []; + const secondaryIndexList: string[] = []; Object.entries(opts?.indices ?? {}).forEach(([index, value]) => { if (value === "primary") { - this._primaryIndexList.push(index); + primaryIndexList.push(index); } else { - this._secondaryIndexList.push(index); + secondaryIndexList.push(index); } }); // Set isIndexable flag - this._isIndexable = this._primaryIndexList.length > 0 || - this._secondaryIndexList.length > 0; + const isIndexable = primaryIndexList.length > 0 || + secondaryIndexList.length > 0; - // Set keepsHistory flag - this._keepsHistory = options?.history ?? false; + this.一internal = { + model, + idGenerator: options?.idGenerator ?? generateId as any, + encoder: options?.encoder, + keys, + primaryIndexList, + secondaryIndexList, + isIndexable, + keepsHistory: options?.history ?? false, + }; } /**********************/ @@ -270,7 +278,7 @@ export class Collection< options?: FindOptions, ): Promise> | null> { // Create document key, get document entry - const key = extendKey(this._keys.id, id); + const key = extendKey(this.一internal.keys.id, id); const entry = await this.kv.get(key, options); return await this.constructDocument(entry); } @@ -297,11 +305,11 @@ export class Collection< options?: FindOptions, ): Promise> | null> { // Serialize and compress index value - const encoded = await encodeData(value, this._encoder); + const encoded = await encodeData(value, this.一internal.encoder); // Create the index key const key = extendKey( - this._keys.primaryIndex, + this.一internal.keys.primaryIndex, index as KvId, encoded, ); @@ -345,11 +353,11 @@ export class Collection< >, ): Promise>>> { // Serialize and compress index value - const encoded = await encodeData(value, this._encoder); + const encoded = await encodeData(value, this.一internal.encoder); // Create prefix key const prefixKey = extendKey( - this._keys.secondaryIndex, + this.一internal.keys.secondaryIndex, index as KvId, encoded, ); @@ -386,7 +394,7 @@ export class Collection< options?: FindManyOptions, ): Promise>[]> { // Create document keys, get document entries - const keys = ids.map((id) => extendKey(this._keys.id, id)); + const keys = ids.map((id) => extendKey(this.一internal.keys.id, id)); const entries = await kvGetMany(keys, this.kv, options); // Create empty result list @@ -434,7 +442,7 @@ export class Collection< ): Promise>> { // Initialize result list and create history key prefix const result: HistoryEntry[] = []; - const keyPrefix = extendKey(this._keys.history, id); + const keyPrefix = extendKey(this.一internal.keys.history, id); const selector = createListSelector(keyPrefix, options); // Create hsitory entries iterator @@ -456,12 +464,12 @@ export class Collection< let historyEntry = value as HistoryEntry; // Handle serialized entries - if (historyEntry.type === "write" && this._encoder) { + if (historyEntry.type === "write" && this.一internal.encoder) { const { ids } = historyEntry.value as EncodedEntry; const timeId = getDocumentId(key as DenoKvStrictKey)!; const keys = ids.map((segmentId) => - extendKey(this._keys.historySegment, id, timeId, segmentId) + extendKey(this.一internal.keys.historySegment, id, timeId, segmentId) ); const entries = await kvGetMany(keys, this.kv); @@ -470,18 +478,18 @@ export class Collection< const data = concat(entries.map((entry) => entry.value as Uint8Array)); // Decompress and deserialize - const decoded = await decodeData(data, this._encoder); + const decoded = await decodeData(data, this.一internal.encoder); // Set history entry historyEntry = { ...historyEntry, - value: this._model.parse?.(decoded), + value: this.一internal.model.parse?.(decoded), }; } else if (historyEntry.type === "write") { // Set history entry historyEntry = { ...historyEntry, - value: this._model.parse?.(historyEntry.value), + value: this.一internal.model.parse?.(historyEntry.value), }; } @@ -561,7 +569,7 @@ export class Collection< * @returns A promise that resovles to void. */ async delete(...ids: ParseId[]): Promise { - await this.deleteDocuments(ids, this._keepsHistory); + await this.deleteDocuments(ids, this.一internal.keepsHistory); } /** @@ -586,11 +594,11 @@ export class Collection< options?: FindOptions, ): Promise { // Serialize and compress index value - const encoded = await encodeData(value, this._encoder); + const encoded = await encodeData(value, this.一internal.encoder); // Create index key const key = extendKey( - this._keys.primaryIndex, + this.一internal.keys.primaryIndex, index as KvId, encoded, ); @@ -609,7 +617,7 @@ export class Collection< & Pick, "__id__">; // Delete document by id - await this.deleteDocuments([__id__], this._keepsHistory); + await this.deleteDocuments([__id__], this.一internal.keepsHistory); } /** @@ -642,11 +650,11 @@ export class Collection< >, ): Promise { // Serialize and compress index value - const encoded = await encodeData(value, this._encoder); + const encoded = await encodeData(value, this.一internal.encoder); // Create prefix key const prefixKey = extendKey( - this._keys.secondaryIndex, + this.一internal.keys.secondaryIndex, index as KvId, encoded, ); @@ -654,7 +662,7 @@ export class Collection< // Delete documents by secondary index, return iterator cursor const { cursor } = await this.handleMany( prefixKey, - (doc) => this.deleteDocuments([doc.id], this._keepsHistory), + (doc) => this.deleteDocuments([doc.id], this.一internal.keepsHistory), options, ); @@ -804,11 +812,11 @@ export class Collection< > > { // Serialize and compress index value - const encoded = await encodeData(value, this._encoder); + const encoded = await encodeData(value, this.一internal.encoder); // Create prefix key const prefixKey = extendKey( - this._keys.secondaryIndex, + this.一internal.keys.secondaryIndex, index as KvId, encoded, ); @@ -981,7 +989,7 @@ export class Collection< > { // Update each document, add commit result to result list return await this.handleMany( - this._keys.id, + this.一internal.keys.id, (doc) => this.updateDocument(doc, value, options), options, ); @@ -1017,7 +1025,10 @@ export class Collection< > > { // Create prefix key - const prefixKey = extendKey(this._keys.secondaryIndex, order as KvId); + const prefixKey = extendKey( + this.一internal.keys.secondaryIndex, + order as KvId, + ); // Update each document by secondary index, add commit result to result list return await this.handleMany( @@ -1060,7 +1071,7 @@ export class Collection< ): Promise> | DenoKvCommitError> { // Update a single document const { result } = await this.handleMany( - this._keys.id, + this.一internal.keys.id, (doc) => this.updateDocument(doc, data, options), { ...options, take: 1 }, ); @@ -1153,7 +1164,10 @@ export class Collection< options?: T, ): Promise> | DenoKvCommitError> { // Create prefix key - const prefixKey = extendKey(this._keys.secondaryIndex, order as KvId); + const prefixKey = extendKey( + this.一internal.keys.secondaryIndex, + order as KvId, + ); // Update a single document const { result } = await this.handleMany( @@ -1258,7 +1272,10 @@ export class Collection< // Perform quick delete if all documents are to be deleted if (selectsAll(options)) { // Create list iterator and empty keys list, init atomic operation - const iter = await this.kv.list({ prefix: this._keys.base }, options); + const iter = await this.kv.list( + { prefix: this.一internal.keys.base }, + options, + ); const keys: DenoKvStrictKey[] = []; const atomic = new AtomicWrapper(this.kv); @@ -1269,8 +1286,10 @@ export class Collection< } // Set history entries if keeps history - if (this._keepsHistory) { - const historyIter = await this.kv.list({ prefix: this._keys.id }); + if (this.一internal.keepsHistory) { + const historyIter = await this.kv.list({ + prefix: this.一internal.keys.id, + }); for await (const { key } of historyIter) { const id = getDocumentId(key as DenoKvStrictKey); @@ -1278,7 +1297,11 @@ export class Collection< continue; } - const historyKey = extendKey(this._keys.history, id, ulid()); + const historyKey = extendKey( + this.一internal.keys.history, + id, + ulid(), + ); const historyEntry: HistoryEntry = { type: "delete", @@ -1296,8 +1319,8 @@ export class Collection< // Execute delete operation for each document entry const { cursor } = await this.handleMany( - this._keys.id, - (doc) => this.deleteDocuments([doc.id], this._keepsHistory), + this.一internal.keys.id, + (doc) => this.deleteDocuments([doc.id], this.一internal.keepsHistory), options, ); @@ -1332,12 +1355,15 @@ export class Collection< >, ): Promise { // Create prefix key - const prefixKey = extendKey(this._keys.secondaryIndex, order as KvId); + const prefixKey = extendKey( + this.一internal.keys.secondaryIndex, + order as KvId, + ); // Delete documents by secondary index, return iterator cursor const { cursor } = await this.handleMany( prefixKey, - (doc) => this.deleteDocuments([doc.id], this._keepsHistory), + (doc) => this.deleteDocuments([doc.id], this.一internal.keepsHistory), options, ); @@ -1372,7 +1398,7 @@ export class Collection< ): Promise>>> { // Get each document, return result list and current iterator cursor return await this.handleMany( - this._keys.id, + this.一internal.keys.id, (doc) => doc, options, ); @@ -1408,7 +1434,10 @@ export class Collection< ParseId >, ): Promise>>> { - const prefixKey = extendKey(this._keys.secondaryIndex, order as KvId); + const prefixKey = extendKey( + this.一internal.keys.secondaryIndex, + order as KvId, + ); return await this.handleMany( prefixKey, (doc) => doc, @@ -1446,7 +1475,7 @@ export class Collection< ): Promise> | null> { // Get result list with one item const { result } = await this.handleMany( - this._keys.id, + this.一internal.keys.id, (doc) => doc, { ...options, take: 1 }, ); @@ -1532,7 +1561,10 @@ export class Collection< >, ): Promise> | null> { // Create prefix key - const prefixKey = extendKey(this._keys.secondaryIndex, order as KvId); + const prefixKey = extendKey( + this.一internal.keys.secondaryIndex, + order as KvId, + ); // Get result list with one item const { result } = await this.handleMany( @@ -1574,7 +1606,7 @@ export class Collection< ): Promise { // Execute callback function for each document entry const { cursor } = await this.handleMany( - this._keys.id, + this.一internal.keys.id, async (doc) => await fn(doc), options, ); @@ -1663,7 +1695,10 @@ export class Collection< >, ): Promise { // Create prefix key - const prefixKey = extendKey(this._keys.secondaryIndex, order as KvId); + const prefixKey = extendKey( + this.一internal.keys.secondaryIndex, + order as KvId, + ); // Execute callback function for each document entry const { cursor } = await this.handleMany( @@ -1707,7 +1742,7 @@ export class Collection< ): Promise>> { // Execute callback function for each document entry, return result and cursor return await this.handleMany( - this._keys.id, + this.一internal.keys.id, (doc) => fn(doc), options, ); @@ -1749,11 +1784,11 @@ export class Collection< >, ): Promise>> { // Serialize and compress index value - const encoded = await encodeData(value, this._encoder); + const encoded = await encodeData(value, this.一internal.encoder); // Create prefix key const prefixKey = extendKey( - this._keys.secondaryIndex, + this.一internal.keys.secondaryIndex, index as KvId, encoded, ); @@ -1799,7 +1834,10 @@ export class Collection< >, ): Promise>> { // Create prefix key - const prefixKey = extendKey(this._keys.secondaryIndex, order as KvId); + const prefixKey = extendKey( + this.一internal.keys.secondaryIndex, + order as KvId, + ); // Execute callback function for each document entry, return result and cursor return await this.handleMany( @@ -1837,7 +1875,10 @@ export class Collection< // Perform efficient count if counting all document entries if (selectsAll(options)) { - const iter = await this.kv.list({ prefix: this._keys.id }, options); + const iter = await this.kv.list( + { prefix: this.一internal.keys.id }, + options, + ); for await (const _ of iter) { result++; } @@ -1845,7 +1886,7 @@ export class Collection< } // Perform count using many documents handler - await this.handleMany(this._keys.id, () => result++, options); + await this.handleMany(this.一internal.keys.id, () => result++, options); return result; } @@ -1875,11 +1916,11 @@ export class Collection< >, ): Promise { // Serialize and compress index value - const encoded = await encodeData(value, this._encoder); + const encoded = await encodeData(value, this.一internal.encoder); // Create prefix key const prefixKey = extendKey( - this._keys.secondaryIndex, + this.一internal.keys.secondaryIndex, index as KvId, encoded, ); @@ -1925,7 +1966,10 @@ export class Collection< >, ): Promise { // Create prefix key - const prefixKey = extendKey(this._keys.secondaryIndex, order as KvId); + const prefixKey = extendKey( + this.一internal.keys.secondaryIndex, + order as KvId, + ); // Initialize count result let result = 0; @@ -1969,8 +2013,8 @@ export class Collection< ): Promise { // Prepare message and options for enqueue const prep = prepareEnqueue( - this._keys.base, - this._keys.undelivered, + this.一internal.keys.base, + this.一internal.keys.undelivered, data, options, ); @@ -2011,7 +2055,10 @@ export class Collection< options?: QueueListenerOptions, ): Promise { // Create handler id - const handlerId = createHandlerId(this._keys.base, options?.topic); + const handlerId = createHandlerId( + this.一internal.keys.base, + options?.topic, + ); // Add new handler to specified handlers const handlers = this.queueHandlers.get(handlerId) ?? []; @@ -2043,7 +2090,7 @@ export class Collection< options?: FindOptions, ): Promise | null> { // Create document key, get document entry - const key = extendKey(this._keys.undelivered, id); + const key = extendKey(this.一internal.keys.undelivered, id); const result = await this.kv.get(key, options); // If no entry exists, return null @@ -2072,8 +2119,11 @@ export class Collection< async deleteHistory(id: ParseId): Promise { // Initialize atomic operation and create iterators const atomic = new AtomicWrapper(this.kv); - const historyKeyPrefix = extendKey(this._keys.history, id); - const historySegmentKeyPrefix = extendKey(this._keys.historySegment, id); + const historyKeyPrefix = extendKey(this.一internal.keys.history, id); + const historySegmentKeyPrefix = extendKey( + this.一internal.keys.historySegment, + id, + ); const historyIter = await this.kv.list({ prefix: historyKeyPrefix }); const historySegmentIter = await this.kv.list({ prefix: historySegmentKeyPrefix, @@ -2104,7 +2154,7 @@ export class Collection< * @param id - Id of undelivered document. */ async deleteUndelivered(id: KvId): Promise { - const key = extendKey(this._keys.undelivered, id); + const key = extendKey(this.一internal.keys.undelivered, id); await this.kv.delete(key); } @@ -2142,7 +2192,7 @@ export class Collection< fn: (doc: Document> | null) => unknown, options?: WatchOptions, ): WatchManager { - const key = extendKey(this._keys.id, id); + const key = extendKey(this.一internal.keys.id, id); return createWatcher(this.kv, options, [key], async (entries) => { const entry = entries.at(0); @@ -2200,7 +2250,7 @@ export class Collection< fn: (doc: (Document> | null)[]) => unknown, options?: WatchOptions, ): WatchManager { - const keys = ids.map((id) => extendKey(this._keys.id, id)); + const keys = ids.map((id) => extendKey(this.一internal.keys.id, id)); return createWatcher(this.kv, options, keys, async (entries) => { // Construct documents @@ -2234,11 +2284,11 @@ export class Collection< options: SetOptions | undefined, ): Promise> | DenoKvCommitError> { // Create id, document key and parse document value - const parsed = this._model._transform?.(value as TInput) ?? - this._model.parse(value); + const parsed = this.一internal.model._transform?.(value as TInput) ?? + this.一internal.model.parse(value); - const docId = id ?? await this._idGenerator(parsed); - const idKey = extendKey(this._keys.id, docId); + const docId = id ?? await this.一internal.idGenerator(parsed); + const idKey = extendKey(this.一internal.keys.id, docId); return await this.setDoc(docId, idKey, parsed, options); } @@ -2275,24 +2325,24 @@ export class Collection< } // Serialize if enabled - if (this._encoder) { + if (this.一internal.encoder) { const encoded = isUint8Array - ? await this._encoder.compressor?.compress(value) ?? value - : await encodeData(value, this._encoder); + ? await this.一internal.encoder.compressor?.compress(value) ?? value + : await encodeData(value, this.一internal.encoder); // Set segmented entries let index = 0; 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); + const key = extendKey(this.一internal.keys.segment, docId, index); ids.push(index); operationPool.set(key, part, options); // Set history segments if keeps history - if (this._keepsHistory) { + if (this.一internal.keepsHistory) { const historySegmentKey = extendKey( - this._keys.historySegment, + this.一internal.keys.historySegment, docId, timeId, index, @@ -2317,8 +2367,8 @@ export class Collection< operationPool.set(idKey, docValue, options); // Set history entry if keeps history - if (this._keepsHistory) { - const historyKey = extendKey(this._keys.history, docId, timeId); + if (this.一internal.keepsHistory) { + const historyKey = extendKey(this.一internal.keys.history, docId, timeId); const historyEntry: HistoryEntry = { type: "write", @@ -2330,7 +2380,7 @@ export class Collection< } // Set indices if is indexable - if (this._isIndexable) { + if (this.一internal.isIndexable) { await setIndices( docId, value as KvObject, @@ -2373,19 +2423,25 @@ export class Collection< if (options?.batched && indexCheck) { const failedAtomic = new AtomicWrapper(this.kv); - if (this._keepsHistory) { - const historyKey = extendKey(this._keys.history, docId, timeId); + if (this.一internal.keepsHistory) { + const historyKey = extendKey( + this.一internal.keys.history, + docId, + timeId, + ); failedAtomic.delete(historyKey); } - if (this._encoder) { + if (this.一internal.encoder) { const { ids } = docValue as EncodedEntry; ids.forEach((id) => - failedAtomic.delete(extendKey(this._keys.segment, docId, id)) + failedAtomic.delete( + extendKey(this.一internal.keys.segment, docId, id), + ) ); } - if (this._isIndexable) { + if (this.一internal.isIndexable) { await deleteIndices( docId, value as KvObject, @@ -2436,7 +2492,7 @@ export class Collection< const { value, id } = doc; // If indexable, check for index collisions and delete exisitng index entries - if (this._isIndexable) { + if (this.一internal.isIndexable) { const atomic = this.kv.atomic(); await checkIndices( @@ -2462,9 +2518,9 @@ export class Collection< } // If serialized, delete existing segment entries - if (this._encoder) { + if (this.一internal.encoder) { const atomic = new AtomicWrapper(this.kv); - const keyPrefix = extendKey(this._keys.segment, id); + const keyPrefix = extendKey(this.一internal.keys.segment, id); const iter = await this.kv.list({ prefix: keyPrefix }); for await (const { key } of iter) { @@ -2489,12 +2545,12 @@ export class Collection< : deepMerge({ value }, { value: data }, options?.mergeOptions).value; // Parse updated value - const parsed = this._model.parse(updated as any); + const parsed = this.一internal.model.parse(updated as any); // Set new document value return await this.setDoc( id, - extendKey(this._keys.id, id), + extendKey(this.一internal.keys.id, id), parsed, { ...options, @@ -2524,12 +2580,12 @@ export class Collection< return null; } - if (this._encoder) { + if (this.一internal.encoder) { // Get document parts const { ids, isUint8Array } = value as EncodedEntry; const keys = ids.map((segId) => - extendKey(this._keys.segment, docId, segId) + extendKey(this.一internal.keys.segment, docId, segId) ); const docEntries = await kvGetMany(keys, this.kv); @@ -2539,11 +2595,12 @@ export class Collection< // Decompress and deserialize const decoded = isUint8Array - ? (await this._encoder?.compressor?.decompress(data) ?? data) as TOutput - : await decodeData(data, this._encoder); + ? (await this.一internal.encoder?.compressor?.decompress(data) ?? + data) as TOutput + : await decodeData(data, this.一internal.encoder); // Return parsed document - return new Document>(this._model, { + return new Document>(this.一internal.model, { id: docId as ParseId, value: decoded, versionstamp, @@ -2553,7 +2610,7 @@ export class Collection< // Remove id from value and return parsed document if indexed entry if (typeof indexedDocId !== "undefined") { const { __id__, ...val } = value as any; - return new Document>(this._model, { + return new Document>(this.一internal.model, { id: docId as ParseId, value: val as TOutput, versionstamp, @@ -2561,7 +2618,7 @@ export class Collection< } // Return parsed document - return new Document>(this._model, { + return new Document>(this.一internal.model, { id: docId as ParseId, value: value as TOutput, versionstamp, @@ -2661,7 +2718,7 @@ export class Collection< // Set delete history entry if recordHistory is true if (recordHistory) { ids.forEach((id) => { - const historyKey = extendKey(this._keys.history, id, ulid()); + const historyKey = extendKey(this.一internal.keys.history, id, ulid()); const historyEntry: HistoryEntry = { type: "delete", @@ -2672,11 +2729,11 @@ export class Collection< }); } - if (this._isIndexable && this._encoder) { + if (this.一internal.isIndexable && this.一internal.encoder) { // Run delete operations for each id await allFulfilled(ids.map(async (id) => { // Create document id key, get entry and construct document - const idKey = extendKey(this._keys.id, id); + const idKey = extendKey(this.一internal.keys.id, id); const entry = await this.kv.get(idKey); const doc = await this.constructDocument(entry); @@ -2685,7 +2742,7 @@ export class Collection< if (entry.value) { const keys = (entry.value as EncodedEntry).ids.map((segId) => - extendKey(this._keys.segment, id, segId) + extendKey(this.一internal.keys.segment, id, segId) ); keys.forEach((key) => atomic.delete(key)); @@ -2701,11 +2758,11 @@ export class Collection< return; } - if (this._isIndexable) { + if (this.一internal.isIndexable) { // Run delete operations for each id await allFulfilled(ids.map(async (id) => { // Create idKey, get document value - const idKey = extendKey(this._keys.id, id); + const idKey = extendKey(this.一internal.keys.id, id); const { value } = await this.kv.get(idKey); // If no value, abort delete @@ -2723,11 +2780,11 @@ export class Collection< return; } - if (this._encoder) { + if (this.一internal.encoder) { // Perform delete for each id await allFulfilled(ids.map(async (id) => { // Create document id key, get document value - const idKey = extendKey(this._keys.id, id); + const idKey = extendKey(this.一internal.keys.id, id); const { value } = await this.kv.get(idKey); // If no value, abort delete @@ -2739,7 +2796,7 @@ export class Collection< atomic.delete(idKey); const keys = (value as EncodedEntry).ids.map((segId) => - extendKey(this._keys.segment, id, segId) + extendKey(this.一internal.keys.segment, id, segId) ); keys.forEach((key) => atomic.delete(key)); @@ -2751,7 +2808,7 @@ export class Collection< } // Perform delete for each id and commit the operation - ids.forEach((id) => atomic.delete(extendKey(this._keys.id, id))); + ids.forEach((id) => atomic.delete(extendKey(this.一internal.keys.id, id))); await atomic.commit(); } } diff --git a/src/utils.ts b/src/utils.ts index 9133e9c..af6b495 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -98,11 +98,11 @@ export async function createSecondaryIndexKeyPrefix( collection: Collection, ) { // Serialize and compress index value - const encoded = await encodeData(value, collection._encoder); + const encoded = await encodeData(value, collection.一internal.encoder); // Create prefix key return extendKey( - collection._keys.secondaryIndex, + collection.一internal.keys.secondaryIndex, index as KvId, encoded, ); @@ -494,14 +494,14 @@ async function handleIndices( secondary?: (indexKey: KvKey) => void, ): Promise { // Handle primary indices - for (const index of collection._primaryIndexList) { + for (const index of collection.一internal.primaryIndexList) { const indexValue = data[index] as KvId | undefined; if (typeof indexValue === "undefined") continue; - const encoded = await encodeData(indexValue, collection._encoder); + const encoded = await encodeData(indexValue, collection.一internal.encoder); const indexKey = extendKey( - collection._keys.primaryIndex, + collection.一internal.keys.primaryIndex, index, encoded, ); @@ -514,14 +514,14 @@ async function handleIndices( } // Handle secondary indices - for (const index of collection._secondaryIndexList) { + for (const index of collection.一internal.secondaryIndexList) { const indexValue = data[index] as KvId | undefined; if (typeof indexValue === "undefined") continue; - const encoded = await encodeData(indexValue, collection._encoder); + const encoded = await encodeData(indexValue, collection.一internal.encoder); const indexKey = extendKey( - collection._keys.secondaryIndex, + collection.一internal.keys.secondaryIndex, index, encoded, id, diff --git a/tests/collection/add.test.ts b/tests/collection/add.test.ts index 67c8799..bd476e4 100644 --- a/tests/collection/add.test.ts +++ b/tests/collection/add.test.ts @@ -7,7 +7,6 @@ Deno.test("collection - add", async (t) => { await useDb(async (db) => { const cr = await db.users.add(mockUser1); assert(cr.ok); - const doc = await db.users.find(cr.id); assert(doc !== null); assert(doc.value.username === mockUser1.username); diff --git a/tests/collection/enqueue.test.ts b/tests/collection/enqueue.test.ts index 2a3b668..19995c2 100644 --- a/tests/collection/enqueue.test.ts +++ b/tests/collection/enqueue.test.ts @@ -21,7 +21,10 @@ Deno.test("collection - enqueue", async (t) => { schema: { numbers: collection(model()) }, }); - const handlerId = createHandlerId(db.numbers._keys.base, undefined); + const handlerId = createHandlerId( + db.numbers.一internal.keys.base, + undefined, + ); let assertion = false; diff --git a/tests/collection/listenQueue.test.ts b/tests/collection/listenQueue.test.ts index e33bd2b..dcae079 100644 --- a/tests/collection/listenQueue.test.ts +++ b/tests/collection/listenQueue.test.ts @@ -27,7 +27,10 @@ Deno.test("collection - listenQueue", async (t) => { }, }); - const handlerId = createHandlerId(db.numbers._keys.base, undefined); + const handlerId = createHandlerId( + db.numbers.一internal.keys.base, + undefined, + ); let assertion = false; diff --git a/tests/collection/properties.test.ts b/tests/collection/properties.test.ts index a4680fa..1dd67bc 100644 --- a/tests/collection/properties.test.ts +++ b/tests/collection/properties.test.ts @@ -10,8 +10,8 @@ import { sleep } from "../utils.ts"; Deno.test("collection - properties", async (t) => { await t.step("Keys should have the correct prefixes", async () => { await useDb((db) => { - const baseKey = db.users._keys.base; - const idKey = db.users._keys.id; + const baseKey = db.users.一internal.keys.base; + const idKey = db.users.一internal.keys.id; const prefix = extendKey([KVDEX_KEY_PREFIX], "users"); assert(keyEq(baseKey, prefix)); @@ -36,8 +36,8 @@ Deno.test("collection - properties", async (t) => { }, }); - const id1 = db.users1._idGenerator(mockUser1); - const id2 = db.users2._idGenerator(mockUser1); + const id1 = db.users1.一internal.idGenerator(mockUser1); + const id2 = db.users2.一internal.idGenerator(mockUser1); assert(typeof id1 === "number"); assert(id2 === mockUser1.username); diff --git a/tests/db/atomic.test.ts b/tests/db/atomic.test.ts index 7e8c4a0..ea840cf 100644 --- a/tests/db/atomic.test.ts +++ b/tests/db/atomic.test.ts @@ -313,7 +313,10 @@ Deno.test("db - atomic", async (t) => { schema: { numbers: collection(model()) }, }); - const handlerId = createHandlerId(db.numbers._keys.base, undefined); + const handlerId = createHandlerId( + db.numbers.一internal.keys.base, + undefined, + ); let assertion = false; diff --git a/tests/db/kvdex.test.ts b/tests/db/kvdex.test.ts index 5c9f347..f96648d 100644 --- a/tests/db/kvdex.test.ts +++ b/tests/db/kvdex.test.ts @@ -18,8 +18,8 @@ Deno.test("db - kvdex", async (t) => { }, }); - const key1 = JSON.stringify(db.numbers._keys.base); - const key2 = JSON.stringify(db.nested.numbers._keys.base); + const key1 = JSON.stringify(db.numbers.一internal.keys.base); + const key2 = JSON.stringify(db.nested.numbers.一internal.keys.base); assert(key1 !== key2); assert(key1 === `["${KVDEX_KEY_PREFIX}","numbers"]`); diff --git a/tests/indexable_collection/enqueue.test.ts b/tests/indexable_collection/enqueue.test.ts index 49895e3..c5f621f 100644 --- a/tests/indexable_collection/enqueue.test.ts +++ b/tests/indexable_collection/enqueue.test.ts @@ -22,7 +22,10 @@ Deno.test("indexable_collection - enqueue", async (t) => { }); const sleeper = createResolver(); - const handlerId = createHandlerId(db.i_users._keys.base, undefined); + const handlerId = createHandlerId( + db.i_users.一internal.keys.base, + undefined, + ); let assertion = false; const listener = kv.listenQueue((msg) => { diff --git a/tests/indexable_collection/listenQueue.test.ts b/tests/indexable_collection/listenQueue.test.ts index f1f2a2d..65b852e 100644 --- a/tests/indexable_collection/listenQueue.test.ts +++ b/tests/indexable_collection/listenQueue.test.ts @@ -26,7 +26,10 @@ Deno.test("indexable_collection - listenQueue", async (t) => { }); const sleeper = createResolver(); - const handlerId = createHandlerId(db.i_users._keys.base, undefined); + const handlerId = createHandlerId( + db.i_users.一internal.keys.base, + undefined, + ); let assertion = false; const listener = db.i_users.listenQueue((msgData) => { diff --git a/tests/indexable_collection/properties.test.ts b/tests/indexable_collection/properties.test.ts index 50f6df2..b169d59 100644 --- a/tests/indexable_collection/properties.test.ts +++ b/tests/indexable_collection/properties.test.ts @@ -22,10 +22,10 @@ import { mockUser3 } from "../mocks.ts"; Deno.test("indexable_collection - properties", async (t) => { await t.step("Keys should have the correct prefixes", async () => { await useDb((db) => { - const baseKey = db.i_users._keys.base; - const idKey = db.i_users._keys.id; - const primaryIndexKey = db.i_users._keys.primaryIndex; - const secondaryIndexKey = db.i_users._keys.secondaryIndex; + const baseKey = db.i_users.一internal.keys.base; + const idKey = db.i_users.一internal.keys.id; + const primaryIndexKey = db.i_users.一internal.keys.primaryIndex; + const secondaryIndexKey = db.i_users.一internal.keys.secondaryIndex; const prefix = extendKey([KVDEX_KEY_PREFIX], "i_users"); assert(keyEq(baseKey, prefix)); @@ -55,8 +55,8 @@ Deno.test("indexable_collection - properties", async (t) => { }, }); - const id1 = db.users1._idGenerator(mockUser1); - const id2 = db.users2._idGenerator(mockUser1); + const id1 = db.users1.一internal.idGenerator(mockUser1); + const id2 = db.users2.一internal.idGenerator(mockUser1); assert(typeof id1 === "number"); assert(id2 === mockUser1.username); diff --git a/tests/serialized_collection/enqueue.test.ts b/tests/serialized_collection/enqueue.test.ts index ccbb42f..7d1f9e9 100644 --- a/tests/serialized_collection/enqueue.test.ts +++ b/tests/serialized_collection/enqueue.test.ts @@ -24,7 +24,10 @@ Deno.test("serialized_collection - enqueue", async (t) => { }); const sleeper = createResolver(); - const handlerId = createHandlerId(db.s_users._keys.base, undefined); + const handlerId = createHandlerId( + db.s_users.一internal.keys.base, + undefined, + ); let assertion = false; const listener = kv.listenQueue((msg) => { diff --git a/tests/serialized_collection/listenQueue.test.ts b/tests/serialized_collection/listenQueue.test.ts index 027effd..3cc56d0 100644 --- a/tests/serialized_collection/listenQueue.test.ts +++ b/tests/serialized_collection/listenQueue.test.ts @@ -33,7 +33,10 @@ Deno.test("serialized_collection - listenQueue", async (t) => { sleeper.resolve(); }); - const handlerId = createHandlerId(db.s_users._keys.base, undefined); + const handlerId = createHandlerId( + db.s_users.一internal.keys.base, + undefined, + ); const msg: QueueMessage = { __is_undefined__: false, diff --git a/tests/serialized_collection/properties.test.ts b/tests/serialized_collection/properties.test.ts index 6607d01..df4ca01 100644 --- a/tests/serialized_collection/properties.test.ts +++ b/tests/serialized_collection/properties.test.ts @@ -16,9 +16,9 @@ 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 () => { await useDb((db) => { - const baseKey = db.s_users._keys.base; - const idKey = db.s_users._keys.id; - const segmentKey = db.s_users._keys.segment; + const baseKey = db.s_users.一internal.keys.base; + const idKey = db.s_users.一internal.keys.id; + const segmentKey = db.s_users.一internal.keys.segment; const prefix = extendKey([KVDEX_KEY_PREFIX], "s_users"); assert(keyEq(baseKey, prefix)); @@ -43,8 +43,8 @@ Deno.test("serialized_collection - properties", async (t) => { }, }); - const id1 = db.users1._idGenerator(mockUser1); - const id2 = db.users2._idGenerator(mockUser1); + const id1 = db.users1.一internal.idGenerator(mockUser1); + const id2 = db.users2.一internal.idGenerator(mockUser1); assert(typeof id1 === "number"); assert(id2 === mockUser1.username); diff --git a/tests/serialized_indexable_collection/enqueue.test.ts b/tests/serialized_indexable_collection/enqueue.test.ts index 9dcf952..a018b54 100644 --- a/tests/serialized_indexable_collection/enqueue.test.ts +++ b/tests/serialized_indexable_collection/enqueue.test.ts @@ -27,7 +27,10 @@ Deno.test("serialized_indexable_collection - enqueue", async (t) => { }, }); - const handlerId = createHandlerId(db.is_users._keys.base, undefined); + const handlerId = createHandlerId( + db.is_users.一internal.keys.base, + undefined, + ); let assertion = false; diff --git a/tests/serialized_indexable_collection/listenQueue.test.ts b/tests/serialized_indexable_collection/listenQueue.test.ts index 5b57eda..2e05913 100644 --- a/tests/serialized_indexable_collection/listenQueue.test.ts +++ b/tests/serialized_indexable_collection/listenQueue.test.ts @@ -32,7 +32,10 @@ Deno.test("serialized_indexable_collection - listenQueue", async (t) => { }, }); - const handlerId = createHandlerId(db.is_users._keys.base, undefined); + const handlerId = createHandlerId( + db.is_users.一internal.keys.base, + undefined, + ); let assertion = false; diff --git a/tests/serialized_indexable_collection/properties.test.ts b/tests/serialized_indexable_collection/properties.test.ts index ac580c8..2cf3793 100644 --- a/tests/serialized_indexable_collection/properties.test.ts +++ b/tests/serialized_indexable_collection/properties.test.ts @@ -26,10 +26,10 @@ const [user] = generateLargeUsers(1); Deno.test("serialized_indexable_collection - properties", async (t) => { await t.step("Keys should have the correct prefixes", async () => { await useDb((db) => { - const baseKey = db.is_users._keys.base; - const idKey = db.is_users._keys.id; - const primaryIndexKey = db.is_users._keys.primaryIndex; - const secondaryIndexKey = db.is_users._keys.secondaryIndex; + const baseKey = db.is_users.一internal.keys.base; + const idKey = db.is_users.一internal.keys.id; + const primaryIndexKey = db.is_users.一internal.keys.primaryIndex; + const secondaryIndexKey = db.is_users.一internal.keys.secondaryIndex; const prefix = extendKey([KVDEX_KEY_PREFIX], "is_users"); assert(keyEq(baseKey, prefix)); @@ -61,8 +61,8 @@ Deno.test("serialized_indexable_collection - properties", async (t) => { }, }); - const id1 = db.users1._idGenerator(user); - const id2 = db.users2._idGenerator(user); + const id1 = db.users1.一internal.idGenerator(user); + const id2 = db.users2.一internal.idGenerator(user); assert(typeof id1 === "number"); assert(id2 === user.username); From da7a0ac9a858b7b9cd09668ac75853bb140502c6 Mon Sep 17 00:00:00 2001 From: Oliver O'Loughlin Date: Fri, 7 Feb 2025 18:17:00 +0100 Subject: [PATCH 2/4] chore: bumped version --- deno.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deno.json b/deno.json index e7cc965..741af34 100644 --- a/deno.json +++ b/deno.json @@ -1,6 +1,6 @@ { "name": "@olli/kvdex", - "version": "3.1.0", + "version": "3.1.1", "exports": { ".": "./mod.ts", "./zod": "./src/ext/zod/mod.ts", From 1999fda19f645266036cb976ca4c5f61d1056e76 Mon Sep 17 00:00:00 2001 From: Oliver O'Loughlin Date: Fri, 7 Feb 2025 18:46:50 +0100 Subject: [PATCH 3/4] fix: added internals type --- deno.json | 2 +- src/collection.ts | 15 ++------------- src/types.ts | 16 ++++++++++++++++ 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/deno.json b/deno.json index 741af34..b22e86b 100644 --- a/deno.json +++ b/deno.json @@ -1,6 +1,6 @@ { "name": "@olli/kvdex", - "version": "3.1.1", + "version": "3.1.2-rc.2", "exports": { ".": "./mod.ts", "./zod": "./src/ext/zod/mod.ts", diff --git a/src/collection.ts b/src/collection.ts index dfa0089..52d23e7 100644 --- a/src/collection.ts +++ b/src/collection.ts @@ -1,7 +1,7 @@ import type { BuilderFn, CheckKeyOf, - CollectionKeys, + CollectionInternals, CollectionOptions, CommitResult, DenoKv, @@ -10,14 +10,12 @@ import type { DenoKvEntryMaybe, DenoKvStrictKey, EncodedEntry, - Encoder, EnqueueOptions, FindManyOptions, FindOptions, HandleOneOptions, HistoryEntry, IdempotentListener, - IdGenerator, IdUpsert, IndexDataEntry, KvId, @@ -155,16 +153,7 @@ export class Collection< private idempotentListener: IdempotentListener; /** Used for internal workings, do not manipulate or rely on these properties. */ - readonly 一internal: { - readonly model: Model; - readonly primaryIndexList: string[]; - readonly secondaryIndexList: string[]; - readonly keys: CollectionKeys; - readonly idGenerator: IdGenerator>; - readonly encoder?: Encoder; - readonly isIndexable: boolean; - readonly keepsHistory: boolean; - }; + readonly 一internal: CollectionInternals; constructor( kv: DenoKv, diff --git a/src/types.ts b/src/types.ts index 35af534..7f4f004 100644 --- a/src/types.ts +++ b/src/types.ts @@ -286,6 +286,22 @@ export type AtomicSetOptions> = /* */ /************************/ +/** Used for internal workings, do not manipulate or rely on these properties. */ +export type CollectionInternals< + TInput, + TOutput extends KvValue, + TOptions extends CollectionOptions, +> = { + readonly model: Model; + readonly primaryIndexList: string[]; + readonly secondaryIndexList: string[]; + readonly keys: CollectionKeys; + readonly idGenerator: IdGenerator>; + readonly encoder?: Encoder; + readonly isIndexable: boolean; + readonly keepsHistory: boolean; +}; + /** Options for creating a new collection */ export type CollectionOptions = & { From ee209c1a4c1e8024baaa8253df5f43040ff133ce Mon Sep 17 00:00:00 2001 From: Oliver O'Loughlin Date: Fri, 7 Feb 2025 18:50:30 +0100 Subject: [PATCH 4/4] chore: bumped version --- deno.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deno.json b/deno.json index b22e86b..2cf2989 100644 --- a/deno.json +++ b/deno.json @@ -1,6 +1,6 @@ { "name": "@olli/kvdex", - "version": "3.1.2-rc.2", + "version": "3.1.2", "exports": { ".": "./mod.ts", "./zod": "./src/ext/zod/mod.ts",