Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion packages/plexus-core/src/collection/collection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ export interface PlexusCollectionConfig<DataType> {
* @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
}
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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
Expand Down
31 changes: 27 additions & 4 deletions packages/plexus-core/src/collection/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@ export type PlexusGroupWatcher<V extends any = any> = (
) => 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<
Expand All @@ -45,6 +46,7 @@ export class CollectionData<
private _internalStore: PlexusDataStore
private foreignKeyData: Record<string | number | symbol, any> = {}
private watchingForeignData: Map<string, () => void>
private decayTimeout?: ReturnType<typeof setTimeout>

constructor(
instance: () => PlexusInstance,
Expand All @@ -55,20 +57,23 @@ 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()

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
Expand Down Expand Up @@ -333,6 +338,24 @@ 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',
`Data ${this.instanceId} decaying in ${time}ms...`
)
if (this.decayTimeout) clearTimeout(this.decayTimeout)
if (!time) return this

this.decayTimeout = setTimeout(() => {
this.delete()
}, time)
return this
}
/**
* Watch for changes on this data instance
* @callback WatchCallback
Expand Down
25 changes: 24 additions & 1 deletion tests/collection.test.ts
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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?', () => {
Expand Down
9 changes: 9 additions & 0 deletions tests/test-utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,15 @@ export const uniqueGroups = collection<UserLiteExplicitIdName>({
uniqueGroups: true
}).createSelector('batched')

export const DEFAULT_DECAY_RATE = 12_000
export const decayingUsers = collection<UserLiteExplicitIdName>({
primaryKey: 'userId',
name: 'userslite',
defaultGroup: 'upcoming',
uniqueGroups: true,
decay: DEFAULT_DECAY_RATE
}).createSelector('batched')

export const appointments = collection<Appointment>({
primaryKey: 'id',
name: 'appointments',
Expand Down