From 7fc333318c2d62c5c6554507b8dd298b6bdbd3ab Mon Sep 17 00:00:00 2001 From: Philippe Clesca Date: Sun, 20 Aug 2023 13:35:37 -0400 Subject: [PATCH 1/3] Ephemeral data, done! --- .../plexus-core/src/collection/collection.ts | 11 +++++++- packages/plexus-core/src/collection/data.ts | 22 +++++++++++++--- tests/collection.test.ts | 25 ++++++++++++++++++- tests/test-utils.tsx | 9 +++++++ 4 files changed, 61 insertions(+), 6 deletions(-) diff --git a/packages/plexus-core/src/collection/collection.ts b/packages/plexus-core/src/collection/collection.ts index 97ff10cb..9ee05409 100644 --- a/packages/plexus-core/src/collection/collection.ts +++ b/packages/plexus-core/src/collection/collection.ts @@ -75,6 +75,10 @@ export interface PlexusCollectionConfig { * @default false * */ uniqueGroups?: boolean + /** + * How long should a data item be allowed to be stale. will automatically clear and remove the data item this many milliseconds after it was last updated + */ + decay?: number sort?: (a: DataType, b: DataType) => number } @@ -283,7 +287,11 @@ export class CollectionInstance< () => this, this._internalStore._key, dataKey, - { ...item, [this._internalStore._key]: dataKey } + { + ...item, + [this._internalStore._key]: dataKey, + }, + { decay: this.config.decay } ) // if we get a valid data instance, add it to the collection if (dataInstance) { @@ -426,6 +434,7 @@ export class CollectionInstance< { prov: true, unfoundKeyIsUndefined: !this.config.unfoundKeyReturnsProvisional, + decay: this.config.decay, } ) // if we get an invalid data instance, return undefined diff --git a/packages/plexus-core/src/collection/data.ts b/packages/plexus-core/src/collection/data.ts index 3a1288c3..f91213ae 100644 --- a/packages/plexus-core/src/collection/data.ts +++ b/packages/plexus-core/src/collection/data.ts @@ -14,13 +14,14 @@ export type PlexusGroupWatcher = ( ) => void interface CollectionDataConfig { - prov: boolean + prov?: boolean unfoundKeyIsUndefined?: boolean + decay?: number } interface PlexusDataStore { primaryKey: string _wDestroyers: Set<() => void> - _config: CollectionDataConfig + config: CollectionDataConfig } export type PlexusDataInstance< @@ -55,7 +56,7 @@ export class CollectionData< config: CollectionDataConfig = { prov: false } ) { super(instance, value) - this.provisional = config.prov + this.provisional = config.prov ?? false this.primaryKey = primaryKey this.key = keyValue + '' this.watchingForeignData = new Map() @@ -63,12 +64,15 @@ export class CollectionData< this._internalStore = { primaryKey, _wDestroyers: new Set<() => void>(), - _config: config, + config: config, } if (!this.provisional) { this.mount() this.syncForeignKeyData(true) } + if (config.decay) { + this.decay(config.decay) + } } /** * The internal id of the state with an instance prefix @@ -333,6 +337,16 @@ export class CollectionData< this.instance()._collectionData.delete(this) return this } + decay(time: number) { + this.instance().runtime.log( + 'debug', + `Data ${this.instanceId} decaying in ${time}ms...` + ) + setTimeout(() => { + this.delete() + }, time) + return this + } /** * Watch for changes on this data instance * @callback WatchCallback diff --git a/tests/collection.test.ts b/tests/collection.test.ts index fae5c86d..80ab630b 100644 --- a/tests/collection.test.ts +++ b/tests/collection.test.ts @@ -1,6 +1,13 @@ import { beforeEach, afterEach, describe, test, expect } from 'vitest' import { collection, instance, PlexusCollectionInstance } from '@plexusjs/core' -import { appointments, users, uniqueGroups } from './test-utils' +import { + appointments, + users, + uniqueGroups, + decayingUsers, + DEFAULT_DECAY_RATE, +} from './test-utils' +import { s } from 'vitest/dist/types-63abf2e0' const myCollection = collection<{ thing: string @@ -205,6 +212,22 @@ describe('Testing Collection', () => { myCollection.collect({ thing: 'lol', id: 0 }) expect(myCollection.size).toBe(2) }) + test( + 'Does decaying data work?', + (ctx) => + new Promise((resolve) => { + decayingUsers.collect({ firstName: 'Bill', userId: '0' }) + expect(decayingUsers.value.length).toBe(1) + expect(decayingUsers.value[0].firstName).toBe('Bill') + expect(decayingUsers.value[0].userId).toBe('0') + setTimeout(() => { + expect(decayingUsers.value.length).toBe(0) + console.log('done! Thing is decayed!') + resolve(true) + }, DEFAULT_DECAY_RATE + 10) + }), + DEFAULT_DECAY_RATE + 100 + ) }) describe('testing collection groups', () => { test('Do Groups Work?', () => { diff --git a/tests/test-utils.tsx b/tests/test-utils.tsx index cb4fad42..78b7c425 100644 --- a/tests/test-utils.tsx +++ b/tests/test-utils.tsx @@ -59,6 +59,15 @@ export const uniqueGroups = collection({ uniqueGroups: true }).createSelector('batched') +export const DEFAULT_DECAY_RATE = 12_000 +export const decayingUsers = collection({ + primaryKey: 'userId', + name: 'userslite', + defaultGroup: 'upcoming', + uniqueGroups: true, + decay: DEFAULT_DECAY_RATE +}).createSelector('batched') + export const appointments = collection({ primaryKey: 'id', name: 'appointments', From 4e5d6ce7b86b84fdccc4a2cd6135755bda7cee01 Mon Sep 17 00:00:00 2001 From: Philippe Clesca Date: Sun, 20 Aug 2023 13:50:58 -0400 Subject: [PATCH 2/3] decay can be removed & changed from a data instance --- packages/plexus-core/src/collection/data.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/plexus-core/src/collection/data.ts b/packages/plexus-core/src/collection/data.ts index f91213ae..0a68e12c 100644 --- a/packages/plexus-core/src/collection/data.ts +++ b/packages/plexus-core/src/collection/data.ts @@ -46,6 +46,7 @@ export class CollectionData< private _internalStore: PlexusDataStore private foreignKeyData: Record = {} private watchingForeignData: Map void> + private decayTimeout?: ReturnType constructor( instance: () => PlexusInstance, @@ -337,12 +338,15 @@ export class CollectionData< this.instance()._collectionData.delete(this) return this } - decay(time: number) { + decay(time: number | false) { this.instance().runtime.log( 'debug', `Data ${this.instanceId} decaying in ${time}ms...` ) - setTimeout(() => { + if (this.decayTimeout) clearTimeout(this.decayTimeout) + if (!time) return this + + this.decayTimeout = setTimeout(() => { this.delete() }, time) return this From ef8f582f3f371191d2f92382986e5ed27c4961c4 Mon Sep 17 00:00:00 2001 From: Philippe Clesca Date: Sun, 20 Aug 2023 13:55:40 -0400 Subject: [PATCH 3/3] jsdoc --- packages/plexus-core/src/collection/data.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/plexus-core/src/collection/data.ts b/packages/plexus-core/src/collection/data.ts index 0a68e12c..621e0198 100644 --- a/packages/plexus-core/src/collection/data.ts +++ b/packages/plexus-core/src/collection/data.ts @@ -338,6 +338,11 @@ export class CollectionData< this.instance()._collectionData.delete(this) return this } + /** + * Decay this data instance after a certain amount of time + * @param {boolean|string}time The time to decay in ms + * @returns {this} The data instance + */ decay(time: number | false) { this.instance().runtime.log( 'debug',