Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/collection-fetchers #70

Open
wants to merge 5 commits into
base: canary
Choose a base branch
from
Open
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
2 changes: 1 addition & 1 deletion .trunk/trunk.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
version: 0.1
cli:
version: 1.15.0
version: 1.16.0
plugins:
sources:
- id: trunk
Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"license": "MIT",
"private": true,
"scripts": {
"postinstall": "test -n \"$NO_YARN_POSTINSTALL\" || yarn run build",
"postinstall": "test -n \"$NO_YARN_POSTINSTALL\" || bun run build",
"core": "bun workspace @plexusjs/core",
"react": "bun workspace @plexusjs/react",
"next": "bun workspace @plexusjs/next",
Expand All @@ -18,9 +18,9 @@
"docs:generate:ref": "bun write-documentation.ts",
"postdocs:generate:ref": "bun format --all",
"bootstrap": "lerna bootstrap --use-workspaces",
"build": "bunx --bun lerna run build",
"build": "bunx lerna run build",
"postbuild": "bun docs:generate:ref",
"dev": "bunx --bun lerna run build && bunx --bun lerna watch -- lerna run build --scope=$LERNA_PACKAGE_NAME",
"dev": "bun --watch run lerna run build",
"prereleaseOnly": "pinst --disable && echo '🚀 Publishing...'",
"release-canary": "lerna run build && lerna publish --preid canary --no-private --yes --force-publish --canary",
"release-stable": "lerna run build && lerna publish --preid canary --no-private --yes --force-publish",
Expand Down
2 changes: 1 addition & 1 deletion packages/plexus-api/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ export class ApiInstance {
this._internalStore.options = deepMerge(
this._internalStore.options,
options
) as RequestInit & { headers: Record<string, string> }
)

options.headers && this.setHeaders(options.headers)
return this
Expand Down
92 changes: 69 additions & 23 deletions packages/plexus-core/src/collection/collection.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { PlexusWatchableValueInterpreter } from '@plexusjs/utils'
import { PlexusInstance, instance } from '../instance/instance'
import { PlexusInternalWatcher } from '../types'
import {
CollectionFetcher,
CollectionSorter,
PlexusInternalWatcher,
} from '../types'

import { _data, PlexusDataInstance, DataKey, CollectionData } from './data'
import {
Expand Down Expand Up @@ -80,15 +84,31 @@ export interface PlexusCollectionConfig<DataType> {
*/
decay?: number

sort?: (a: DataType, b: DataType) => number
/**
* Sort function to use for the collection
* @param a The first item to compare
* @param b The second item to compare
* @returns -1 if a should be before b, 1 if a should be after b, 0 if they are equal
*/
sort?: CollectionSorter<DataType>

/**
* A function used to fetch data for a given collection key. If a call is made to use an item that doesn't exist in the collection (getItem, getItemValue, etc.), this function will be called to fetch the data. If the function returns a promise, the promise will be awaited before returning the data.
* @param key The key of the item to fetch
* @returns The data for the item
*/
dataFetcher?: CollectionFetcher<CollectionData>
}
interface PlexusCollectionStore<DataType extends Record<string, any>> {
_internalId: string
_lastChanged: string
_lookup: Map<string, string>
_key: string
_data: Map<string, PlexusDataInstance<DataType>>
_groups: GroupMap<DataType>
_data: Map<
string,
PlexusDataInstance<PlexusWatchableValueInterpreter<DataType>>
>
_groups: GroupMap<PlexusWatchableValueInterpreter<DataType>>
_selectors: SelectorMap<DataType>
_name: string
_externalName: string
Expand All @@ -99,22 +119,27 @@ interface PlexusCollectionStore<DataType extends Record<string, any>> {
_computeFn?: (
data: PlexusWatchableValueInterpreter<DataType>
) => PlexusWatchableValueInterpreter<DataType>
sort?: (a: DataType, b: DataType) => number
sort?: CollectionSorter<DataType>
dataFetcher?: CollectionFetcher<CollectionData>
}

export type PlexusCollectionInstance<
DataType extends Record<string, any> = Record<string, any>,
Groups extends GroupMap<DataType> = GroupMap<DataType>,
Selectors extends SelectorMap<DataType> = SelectorMap<DataType>,
Groups extends GroupMap<PlexusWatchableValueInterpreter<DataType>> = GroupMap<
PlexusWatchableValueInterpreter<DataType>
>,
Selectors extends SelectorMap<
PlexusWatchableValueInterpreter<DataType>
> = SelectorMap<PlexusWatchableValueInterpreter<DataType>>
> = CollectionInstance<DataType, Groups, Selectors>
/**
* A Collection Instance
*
*/
export class CollectionInstance<
DataTypeInput extends Record<string, any>,
Groups extends GroupMap<DataTypeInput>,
Selectors extends SelectorMap<DataTypeInput>,
Groups extends GroupMap<PlexusWatchableValueInterpreter<DataTypeInput>>,
Selectors extends SelectorMap<PlexusWatchableValueInterpreter<DataTypeInput>>
// ForeignRefs extends boolean = this['config']['foreignKeys'] extends {} ? true : false
> {
private _internalStore: PlexusCollectionStore<DataTypeInput>
Expand Down Expand Up @@ -160,10 +185,13 @@ export class CollectionInstance<
_lookup: new Map<string, string>(),
_lastChanged: '',
_key: config?.primaryKey || 'id',
_data: new Map<string, PlexusDataInstance<DataTypeInput>>(),
_data: new Map<
string,
PlexusDataInstance<PlexusWatchableValueInterpreter<DataTypeInput>>
>(),
_groups: new Map<
GroupName,
PlexusCollectionGroup<DataTypeInput>
PlexusCollectionGroup<PlexusWatchableValueInterpreter<DataTypeInput>>
>() as Groups,
_selectors: new Map<
SelectorName,
Expand All @@ -180,6 +208,7 @@ export class CollectionInstance<
this._persist = value
},
sort: config.sort,
dataFetcher: config.dataFetcher,
}
this.mount()

Expand Down Expand Up @@ -591,13 +620,17 @@ export class CollectionInstance<
* @param {string} name The Group Name to search for
* @returns {this} The new Collection Instance
*/
getGroup(name: GroupName): PlexusCollectionGroup<DataTypeInput>
getGroup(name: KeyOfMap<Groups>): PlexusCollectionGroup<DataTypeInput>
getGroup(
name: GroupName
): PlexusCollectionGroup<PlexusWatchableValueInterpreter<DataTypeInput>>
getGroup(
name: KeyOfMap<Groups>
): PlexusCollectionGroup<PlexusWatchableValueInterpreter<DataTypeInput>>
getGroup(name: KeyOfMap<Groups> | GroupName) {
if (this.isCreatedGroup(name)) {
const group = this._internalStore._groups.get(
name
) as PlexusCollectionGroup<DataTypeInput>
) as PlexusCollectionGroup<PlexusWatchableValueInterpreter<DataTypeInput>>

return group
} else {
Expand Down Expand Up @@ -743,12 +776,21 @@ export class CollectionInstance<
*/
watchGroup(
name: KeyOfMap<Groups>,
callback: PlexusInternalWatcher<DataTypeInput[]>
callback: PlexusInternalWatcher<
PlexusWatchableValueInterpreter<DataTypeInput>[]
>
)
watchGroup(
name: string,
callback: PlexusInternalWatcher<
PlexusWatchableValueInterpreter<DataTypeInput>[]
>
)
watchGroup(name: string, callback: PlexusInternalWatcher<DataTypeInput[]>)
watchGroup(
name: KeyOfMap<Groups> | string,
callback: PlexusInternalWatcher<DataTypeInput[]>
callback: PlexusInternalWatcher<
PlexusWatchableValueInterpreter<DataTypeInput>[]
>
) {
const group = this.getGroup(name)
if (this.isCreatedGroup(name) && group) {
Expand Down Expand Up @@ -923,9 +965,13 @@ export class CollectionInstance<
* Get all of the collection data values as an array
* @type {DataTypeInput[]}
*/
get value(): (DataTypeInput & { [key: string]: any })[] {
get value(): (PlexusWatchableValueInterpreter<DataTypeInput> & {
[key: string]: any
})[] {
this.mount()
const values: (DataTypeInput & { [key: string]: any })[] = []
const values: (PlexusWatchableValueInterpreter<DataTypeInput> & {
[key: string]: any
})[] = []
for (let item of this._internalStore._data.values()) {
if (!item.provisional) {
values.push(item.value)
Expand Down Expand Up @@ -954,10 +1000,10 @@ export class CollectionInstance<
* @type {Record<string, GroupInstance>}
*/
get groups() {
const groups: Record<
const groups = {} as Record<
KeyOfMap<Groups>,
PlexusCollectionGroup<DataTypeInput>
> = {} as Record<KeyOfMap<Groups>, PlexusCollectionGroup<DataTypeInput>>
PlexusCollectionGroup<PlexusWatchableValueInterpreter<DataTypeInput>>
>
for (let group of this._internalStore._groups.entries()) {
groups[group[0] as KeyOfMap<Groups>] = group[1]
}
Expand Down Expand Up @@ -1058,7 +1104,7 @@ export class CollectionInstance<
export function _collection<
DataType extends { [key: string]: any },
Groups extends GroupMap<DataType> = GroupMap<DataType>,
Selectors extends SelectorMap<DataType> = SelectorMap<DataType>,
Selectors extends SelectorMap<DataType> = SelectorMap<DataType>
>(
instance: () => PlexusInstance,
_config: PlexusCollectionConfig<DataType> = { primaryKey: 'id' } as const
Expand Down
29 changes: 25 additions & 4 deletions packages/plexus-core/src/collection/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ interface PlexusDataStore {
}

export type PlexusDataInstance<
DataType extends Record<string, any> = Record<string, any>,
DataType extends Record<string, any> = Record<string, any>
> = CollectionData<DataType>
export type DataKey = string

Expand All @@ -38,7 +38,7 @@ type DataObjectType<PK extends string = 'id'> = Record<string, any> & {
*/
export class CollectionData<
DataType extends DataObjectType<PK> = any,
PK extends string = string,
PK extends string = string
> extends WatchableMutable<DataType> {
private primaryKey: PK
readonly key: string
Expand Down Expand Up @@ -67,6 +67,25 @@ export class CollectionData<
_wDestroyers: new Set<() => void>(),
config: config,
}
let dataFetcher = this.collection().config.dataFetcher
if (this.provisional && dataFetcher) {
this.set(dataFetcher(this.key))
// const data =
// if (data instanceof Promise) {
// // this.loading = true
// // data.then((data) => {
// // if (data) {
// // this.set(data)
// // this.loading = false
// // }
// // })
// this.set(async () => {
// return await data
// })
// } else {
// this.set(data)
// }
}
if (!this.provisional) {
this.mount()
this.syncForeignKeyData(true)
Expand Down Expand Up @@ -253,7 +272,9 @@ export class CollectionData<
* @param {DataType} value The value to set
* @returns {this} The data instance
*/
set(value?: Partial<PlexusWatchableValueInterpreter<DataType>>): this {
set(
value?: PlexusWatchableValueInterpreter<Partial<DataType>> | DataType
): this {
if (!value) return this

// if this is provisional, mount to the collection & instance
Expand Down Expand Up @@ -316,7 +337,7 @@ export class CollectionData<
* @param {DataType} value A value of the state to merge with the current value
* @returns {this} The data instance
*/
patch(value: Partial<PlexusWatchableValueInterpreter<DataType>>): this {
patch(value: PlexusWatchableValueInterpreter<Partial<DataType>>): this {
this.set(deepMerge(this._watchableStore._value, value, true))

this.collection().lastUpdatedKey = this.key
Expand Down
12 changes: 6 additions & 6 deletions packages/plexus-core/src/collection/group.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { PlexusWatchableValueInterpreter } from '@plexusjs/utils'
import { PlexusCollectionInstance } from '..'
import { PlexusInstance } from '../instance/instance'
import { PlexusInternalWatcher } from '../types'
import { CollectionSorter, PlexusInternalWatcher } from '../types'
import { Watchable } from '../watchable'

import { DataKey, PlexusDataInstance } from './data'

export interface PlexusCollectionGroupConfig<DataType> {
addWhen?: (item: PlexusWatchableValueInterpreter<DataType>) => boolean
sort?: (a: DataType, b: DataType) => number
sort?: CollectionSorter<DataType>
}
export type GroupName = string

Expand All @@ -21,15 +21,15 @@ interface CollectionGroupStore<DataType = any> {
_collectionId: string
_includedKeys: Set<string>
_dataWatcherDestroyers: Set<() => void>
sort?: (a: DataType, b: DataType) => number
sort?: CollectionSorter<DataType>
}

/**
* A group of data
*/
export class CollectionGroup<
DataType extends Record<string, any> = any,
> extends Watchable<DataType[]> {
DataType extends Record<string, any> = any
> extends Watchable<PlexusWatchableValueInterpreter<DataType>[]> {
private _internalStore: CollectionGroupStore<DataType>
private collection: () => PlexusCollectionInstance<DataType>
// private instance: () => PlexusInstance
Expand Down Expand Up @@ -80,7 +80,7 @@ export class CollectionGroup<
// memoization: this updates the groups stored value! This reduces computation as the state of the group is only updated when the data changes
this._watchableStore._publicValue = keys
.map((key) => this.collection().getItemValue(key))
.filter(Boolean) as DataType[]
.filter(Boolean) as PlexusWatchableValueInterpreter<DataType>[]

this.instance().runtime.broadcast(this.id, this.value)
}
Expand Down
2 changes: 1 addition & 1 deletion packages/plexus-core/src/instance/engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ export class EventEngine {
const pendingPayload = this.pendingEventPayloads.get(eventId)

const eventPayload = pendingPayload
? deepMerge<EventPayload>(pendingPayload, args)
? deepMerge(pendingPayload, args)
: args
this.pendingEventPayloads.set(eventId, eventPayload)
return
Expand Down
18 changes: 16 additions & 2 deletions packages/plexus-core/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { AlmostAnything } from '@plexusjs/utils'
import {
AlmostAnything,
PlexusWatchableValueInterpreter,
} from '@plexusjs/utils'
import { _event } from './event'
import { _runtime } from './instance/runtime'

Expand All @@ -12,4 +15,15 @@ export type PlexusInternalWatcher<V extends any = any> = (
) => void

export type PlexusValidStateTypes = NonNullable<AlmostAnything>
export type Fetcher<Value> = () => Value
export type Fetcher<Value> = () => Value | Promise<Value>

export type CollectionSorter<DataType> = (
a: PlexusWatchableValueInterpreter<DataType>,
b: PlexusWatchableValueInterpreter<DataType>
) => number

export type CollectionFetcher<DataType> = (
key: string
) =>
| Promise<PlexusWatchableValueInterpreter<DataType>>
| PlexusWatchableValueInterpreter<DataType>
Loading