Skip to content

Commit 0fd05d3

Browse files
committed
fixed controllers too
1 parent 6ccd0d4 commit 0fd05d3

File tree

3 files changed

+82
-37
lines changed

3 files changed

+82
-37
lines changed

packages/plexus-core/src/error.ts

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,42 @@
11
import { PlexusInstance } from './instance/instance'
22

3+
type PlexusErrorOptions = { code: string; source: string; stack: string }
4+
35
export class PlexusError extends Error {
46
public name = 'PlexusError'
57
public error = true
6-
constructor(
7-
message: string,
8-
public options?: Partial<{ code: string; origin: string; stack: string }>
9-
) {
8+
constructor(message: string, public options?: Partial<PlexusErrorOptions>) {
109
super(message)
1110
}
1211
// custom error format for logging and debugging
1312
toString() {
14-
return `PlexusError: ${this.message} (${this.options?.code ?? 'NO_CODE'})`
13+
return `PlexusError${
14+
this.options?.source ? `(${this.options.source})` : ''
15+
}${this.options?.code ? `[${this.options?.code}]` : ''}: ${this.message}`
1516
}
1617
}
17-
export function handlePlexusError(e: unknown | Error | string): PlexusError {
18+
export function handlePlexusError(
19+
e: unknown | Error | string,
20+
options?: Partial<PlexusErrorOptions>
21+
): PlexusError {
1822
if (typeof e === 'string') {
19-
return new PlexusError(e)
23+
return new PlexusError(e, options)
2024
}
2125

2226
// error-like objects
2327
if (e instanceof PlexusError) return e
2428
if (e instanceof Error) {
2529
return new PlexusError(
2630
`An error occurred during the execution of an action (${e.message})`,
27-
{ origin: 'action', stack: e.stack }
31+
{ ...options, stack: e.stack }
2832
)
2933
}
3034

3135
// generic error
3236
return new PlexusError(
3337
'An error occurred during the execution of an action',
3438
{
35-
origin: 'action',
39+
...options,
3640
}
3741
)
3842
}
Lines changed: 57 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { PlexusCollectionInstance } from '../collection/collection'
22
import type { PlexusComputedStateInstance } from '../computed'
3+
import { handlePlexusError } from '../error'
34
import type { PlexusEventInstance } from '../event'
45
import type { PlexusStateInstance } from '../state'
56
import { PlexusInstance, instance } from './instance'
@@ -13,56 +14,93 @@ type PlexusItem =
1314
type PlexusRecord<T> = {
1415
[key: string]: T | PlexusItem
1516
}
17+
18+
type PlexusIdMap<T> = {
19+
[key: string]: T | string
20+
}
21+
type ControllerOptions<OriginalObject = PlexusRecord<any>> = {
22+
instance: () => PlexusInstance
23+
id: string
24+
data: OriginalObject
25+
}
1626
/**
1727
* Plexus Controller can be used to create a controller for a module within your plexus core.
1828
* Pass an object that can be many layers deep. Each key is the name of a plexus item (state, action, etc) and the value is the instance of the item.
1929
*/
2030
export class ControllerInstance<OriginalObject extends PlexusRecord<any>> {
21-
public readonly name: string
22-
public readonly path: string
31+
private readonly id: string
32+
private path: string | null = null
2333
public readonly instance: () => PlexusInstance
2434
protected readonly moduleStore: OriginalObject
25-
private linkedIds: Map<string, string[]> = new Map()
35+
private linkedIds: Record<
36+
string,
37+
string | ControllerInstance<PlexusRecord<any>>
38+
> = Object.freeze({})
2639

27-
constructor(
28-
instance: () => PlexusInstance,
29-
name: string,
30-
data: OriginalObject
31-
) {
32-
this.name = name
33-
this.path = name
40+
constructor(public name: string, options: ControllerOptions<OriginalObject>) {
41+
const { instance, id, data } = options
42+
this.id = id
3443
this.instance = instance
3544
this.moduleStore = data
3645
this.create(data)
3746
}
38-
static parseObject(name: string, data: PlexusRecord<any>) {
47+
48+
static parseModule(data: PlexusRecord<any>) {
3949
const names = Object.keys(data)
50+
const values = names.map((name) => this.parseItem(name, data))
51+
return Object.fromEntries(values)
52+
}
53+
54+
static parseItem(name: string, data: PlexusRecord<any>) {
4055
const item = data[name]
41-
const id = item.id
56+
if (item instanceof ControllerInstance) {
57+
return [name, item.linkedIds]
58+
}
59+
const id: string = item?.id
4260
if (!id) {
43-
throw new Error(
44-
`Plexus Controller: ${this.name} - ${name} is missing an id`
45-
)
61+
throw handlePlexusError(`${name} is missing an id`, {
62+
source: this.name,
63+
})
4664
}
4765
item.name = name
48-
return [name, [id]]
66+
return [name, id]
4967
}
5068
/**
5169
* Intake data, get the object id's, link them to the name, assign the names to the instance in the key's value, and save the mapping to the controller
5270
* @param data
5371
*/
5472
create(data: OriginalObject) {
55-
const mapping = ControllerInstance.parseObject(this.name, data)
73+
const mapping = ControllerInstance.parseModule(data)
5674

57-
// this.linkedIds = new Map(mapping)
75+
this.linkedIds = Object.freeze({ ...mapping })
5876
}
5977

6078
get module() {
6179
return this.moduleStore as OriginalObject
6280
}
6381
}
82+
export function controller<OriginalObject extends PlexusRecord<any>>(
83+
name: string,
84+
data: OriginalObject
85+
)
6486
export function controller<OriginalObject extends PlexusRecord<any>>(
6587
data: OriginalObject
88+
)
89+
export function controller<OriginalObject extends PlexusRecord<any>>(
90+
nameOrData: string | OriginalObject,
91+
data?: OriginalObject
6692
) {
67-
return new ControllerInstance(() => instance(), instance().genId(), data)
93+
const id = instance().genId()
94+
if (typeof nameOrData === 'string') {
95+
return new ControllerInstance(nameOrData, {
96+
instance: () => instance(),
97+
id,
98+
data: data || {},
99+
})
100+
}
101+
return new ControllerInstance(id, {
102+
instance: () => instance(),
103+
id,
104+
data: nameOrData,
105+
})
68106
}

tests/controller.test.ts

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { beforeEach, afterEach, describe, test, expect, } from 'vitest'
1+
import { beforeEach, afterEach, describe, test, expect } from 'vitest'
22

33
import {
44
batchAction,
@@ -20,14 +20,17 @@ beforeEach(() => {
2020
})
2121

2222
describe('Controller Basics', () => {
23-
// test('can a controller be used', () => {
24-
// const myModule = controller({
25-
// myState: state('hey there'),
26-
// }).export()
27-
28-
// expect(myModule.myState).toBeDefined()
29-
// expect(myModule.myState.value).toBe('hey there')
30-
// })
23+
test('can a controller be used', () => {
24+
const myController = controller('myController', {
25+
myState: state('hey there'),
26+
})
27+
const myModule = myController.module
28+
29+
expect(myModule.myState).toBeDefined()
30+
expect(myModule.myState.value).toBe('hey there')
31+
32+
expect(myController.name).toBe('myController')
33+
})
3134
test('can a controller be used with .module', () => {
3235
const myModule = controller({
3336
myState: state('hey there'),

0 commit comments

Comments
 (0)