Skip to content

Commit fdb910e

Browse files
authored
Merge pull request #66 from PlexusJS/feat/ephemeral-data
Ephemeral data, done!
2 parents 263e242 + ef8f582 commit fdb910e

File tree

4 files changed

+70
-6
lines changed

4 files changed

+70
-6
lines changed

packages/plexus-core/src/collection/collection.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,10 @@ export interface PlexusCollectionConfig<DataType> {
7575
* @default false
7676
* */
7777
uniqueGroups?: boolean
78+
/**
79+
* 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
80+
*/
81+
decay?: number
7882

7983
sort?: (a: DataType, b: DataType) => number
8084
}
@@ -283,7 +287,11 @@ export class CollectionInstance<
283287
() => this,
284288
this._internalStore._key,
285289
dataKey,
286-
{ ...item, [this._internalStore._key]: dataKey }
290+
{
291+
...item,
292+
[this._internalStore._key]: dataKey,
293+
},
294+
{ decay: this.config.decay }
287295
)
288296
// if we get a valid data instance, add it to the collection
289297
if (dataInstance) {
@@ -426,6 +434,7 @@ export class CollectionInstance<
426434
{
427435
prov: true,
428436
unfoundKeyIsUndefined: !this.config.unfoundKeyReturnsProvisional,
437+
decay: this.config.decay,
429438
}
430439
)
431440
// if we get an invalid data instance, return undefined

packages/plexus-core/src/collection/data.ts

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,14 @@ export type PlexusGroupWatcher<V extends any = any> = (
1414
) => void
1515

1616
interface CollectionDataConfig {
17-
prov: boolean
17+
prov?: boolean
1818
unfoundKeyIsUndefined?: boolean
19+
decay?: number
1920
}
2021
interface PlexusDataStore {
2122
primaryKey: string
2223
_wDestroyers: Set<() => void>
23-
_config: CollectionDataConfig
24+
config: CollectionDataConfig
2425
}
2526

2627
export type PlexusDataInstance<
@@ -45,6 +46,7 @@ export class CollectionData<
4546
private _internalStore: PlexusDataStore
4647
private foreignKeyData: Record<string | number | symbol, any> = {}
4748
private watchingForeignData: Map<string, () => void>
49+
private decayTimeout?: ReturnType<typeof setTimeout>
4850

4951
constructor(
5052
instance: () => PlexusInstance,
@@ -55,20 +57,23 @@ export class CollectionData<
5557
config: CollectionDataConfig = { prov: false }
5658
) {
5759
super(instance, value)
58-
this.provisional = config.prov
60+
this.provisional = config.prov ?? false
5961
this.primaryKey = primaryKey
6062
this.key = keyValue + ''
6163
this.watchingForeignData = new Map()
6264

6365
this._internalStore = {
6466
primaryKey,
6567
_wDestroyers: new Set<() => void>(),
66-
_config: config,
68+
config: config,
6769
}
6870
if (!this.provisional) {
6971
this.mount()
7072
this.syncForeignKeyData(true)
7173
}
74+
if (config.decay) {
75+
this.decay(config.decay)
76+
}
7277
}
7378
/**
7479
* The internal id of the state with an instance prefix
@@ -333,6 +338,24 @@ export class CollectionData<
333338
this.instance()._collectionData.delete(this)
334339
return this
335340
}
341+
/**
342+
* Decay this data instance after a certain amount of time
343+
* @param {boolean|string}time The time to decay in ms
344+
* @returns {this} The data instance
345+
*/
346+
decay(time: number | false) {
347+
this.instance().runtime.log(
348+
'debug',
349+
`Data ${this.instanceId} decaying in ${time}ms...`
350+
)
351+
if (this.decayTimeout) clearTimeout(this.decayTimeout)
352+
if (!time) return this
353+
354+
this.decayTimeout = setTimeout(() => {
355+
this.delete()
356+
}, time)
357+
return this
358+
}
336359
/**
337360
* Watch for changes on this data instance
338361
* @callback WatchCallback

tests/collection.test.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
import { beforeEach, afterEach, describe, test, expect } from 'vitest'
22
import { collection, instance, PlexusCollectionInstance } from '@plexusjs/core'
3-
import { appointments, users, uniqueGroups } from './test-utils'
3+
import {
4+
appointments,
5+
users,
6+
uniqueGroups,
7+
decayingUsers,
8+
DEFAULT_DECAY_RATE,
9+
} from './test-utils'
10+
import { s } from 'vitest/dist/types-63abf2e0'
411

512
const myCollection = collection<{
613
thing: string
@@ -205,6 +212,22 @@ describe('Testing Collection', () => {
205212
myCollection.collect({ thing: 'lol', id: 0 })
206213
expect(myCollection.size).toBe(2)
207214
})
215+
test(
216+
'Does decaying data work?',
217+
(ctx) =>
218+
new Promise((resolve) => {
219+
decayingUsers.collect({ firstName: 'Bill', userId: '0' })
220+
expect(decayingUsers.value.length).toBe(1)
221+
expect(decayingUsers.value[0].firstName).toBe('Bill')
222+
expect(decayingUsers.value[0].userId).toBe('0')
223+
setTimeout(() => {
224+
expect(decayingUsers.value.length).toBe(0)
225+
console.log('done! Thing is decayed!')
226+
resolve(true)
227+
}, DEFAULT_DECAY_RATE + 10)
228+
}),
229+
DEFAULT_DECAY_RATE + 100
230+
)
208231
})
209232
describe('testing collection groups', () => {
210233
test('Do Groups Work?', () => {

tests/test-utils.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,15 @@ export const uniqueGroups = collection<UserLiteExplicitIdName>({
5959
uniqueGroups: true
6060
}).createSelector('batched')
6161

62+
export const DEFAULT_DECAY_RATE = 12_000
63+
export const decayingUsers = collection<UserLiteExplicitIdName>({
64+
primaryKey: 'userId',
65+
name: 'userslite',
66+
defaultGroup: 'upcoming',
67+
uniqueGroups: true,
68+
decay: DEFAULT_DECAY_RATE
69+
}).createSelector('batched')
70+
6271
export const appointments = collection<Appointment>({
6372
primaryKey: 'id',
6473
name: 'appointments',

0 commit comments

Comments
 (0)