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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Every object in the store has a globally unique UUID stored in the id property,

The store is divided into a root store and separate stores defined for each type of object, with actual data stored in these individual object stores.

Each object-specific store implements [EventEmitter](https://nodejs.org/docs/latest/api/events.html#class-eventemitter), allowing the renderer to monitor changes to the store through events.
Each object-specific store extends [`CCBaseStore`](/src/store/base.ts), which implements [EventEmitter](https://nodejs.org/docs/latest/api/events.html#class-eventemitter), allowing the renderer to monitor changes to the store through events.
Each object-specific store must implement the following four events, with the event object passing the object that triggered the event:

- `didRegister`: Triggered when an object is registered in the store.
Expand Down
45 changes: 45 additions & 0 deletions src/store/base.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import EventEmitter from "eventemitter3";
import type CCStore from ".";

/** Events emitted by every object-specific store */
export type CCBaseStoreEvents<T> = {
didRegister(object: T): void;
willUnregister(object: T): void;
didUnregister(object: T): void;
};

/** Base class of the object-specific stores (components, nodes, pins, and connections) */
export abstract class CCBaseStore<
Id extends string,
T extends { readonly id: Id },
Events extends CCBaseStoreEvents<T>,
> extends EventEmitter<Events> {
protected readonly store: CCStore;

protected readonly objects: Map<Id, T> = new Map();

constructor(store: CCStore) {
super();
this.store = store;
}

/** Subscribe to the events of other stores. Called after all stores are constructed. */
abstract mount(): void;

/**
* Get an object by id
* @param id id of object
* @returns object of `id`
*/
get(id: Id): T | undefined {
return this.objects.get(id);
}

/**
* Get array of objects
* @returns array of objects
*/
getMany(): T[] {
return [...this.objects.values()];
}
}
60 changes: 14 additions & 46 deletions src/store/component.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import EventEmitter from "eventemitter3";
import nullthrows from "nullthrows";
import invariant from "tiny-invariant";
import type { Tagged } from "type-fest";
import type CCStore from ".";
import { CCBaseStore, type CCBaseStoreEvents } from "./base";
import type { CCIntrinsicComponentType } from "./intrinsics/types";

export type CCComponentId = Tagged<string, "CCComponentId">;
Expand All @@ -14,10 +14,7 @@ export type CCComponent = {
name: string;
};

export type CCComponentStoreEvents = {
didRegister(component: CCComponent): void;
willUnregister(component: CCComponent): void;
didUnregister(component: CCComponent): void;
export type CCComponentStoreEvents = CCBaseStoreEvents<CCComponent> & {
didUpdate(component: CCComponent): void;
};
export const ccComponentStoreChangeEventTypes: (keyof CCComponentStoreEvents)[] =
Expand All @@ -26,23 +23,11 @@ export const ccComponentStoreChangeEventTypes: (keyof CCComponentStoreEvents)[]
/**
* Store of components
*/
export class CCComponentStore extends EventEmitter<CCComponentStoreEvents> {
#store: CCStore;

#components: Map<CCComponentId, CCComponent> = new Map();

/**
* Constructor of CCComponentStore
* @param store store
* @param rootComponent root component
* @param rootComponentId id of root component
* @param components initial components
*/
constructor(store: CCStore) {
super();
this.#store = store;
}

export class CCComponentStore extends CCBaseStore<
CCComponentId,
CCComponent,
CCComponentStoreEvents
> {
import(components: CCComponent[]) {
for (const component of components) {
this.register(component);
Expand All @@ -56,8 +41,8 @@ export class CCComponentStore extends EventEmitter<CCComponentStoreEvents> {
* @param component component to be registered
*/
register(component: CCComponent): void {
invariant(!this.#components.has(component.id));
this.#components.set(component.id, component);
invariant(!this.objects.has(component.id));
this.objects.set(component.id, component);
this.emit("didRegister", component);
}

Expand All @@ -66,33 +51,24 @@ export class CCComponentStore extends EventEmitter<CCComponentStoreEvents> {
* @param id id of a component to be unregistered
*/
async unregister(id: CCComponentId): Promise<void> {
const component = this.#components.get(id);
const component = this.objects.get(id);
if (!component) throw new Error(`Component ${id} not found`);
await this.#store.transactionManager.runInTransaction(() => {
await this.store.transactionManager.runInTransaction(() => {
this.emit("willUnregister", component);
this.#components.delete(id);
this.objects.delete(id);
});
this.emit("didUnregister", component);
}

/**
* Get a component by CCComponentId
* @param id id of component
* @returns component of `id`
*/
get(id: CCComponentId): CCComponent | undefined {
return this.#components.get(id);
}

/**
* Update the name of component
* @param id id of component
* @param value new name
*/
update(id: CCComponentId, value: Pick<CCComponent, "name">): void {
const component = this.#components.get(id);
const component = this.objects.get(id);
invariant(component);
this.#components.set(id, { ...component, ...value });
this.objects.set(id, { ...component, ...value });
this.emit("didUpdate", component);
}

Expand All @@ -110,14 +86,6 @@ export class CCComponentStore extends EventEmitter<CCComponentStoreEvents> {
...partialComponent,
};
}

/**
* Get array of components
* @returns array of components
*/
getMany(): CCComponent[] {
return [...this.#components.values()];
}
}

/**
Expand Down
85 changes: 26 additions & 59 deletions src/store/componentPin.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import EventEmitter from "eventemitter3";
import nullthrows from "nullthrows";
import invariant from "tiny-invariant";
import type { Tagged } from "type-fest";
import type CCStore from ".";
import { CCBaseStore, type CCBaseStoreEvents } from "./base";
import type { CCComponentId } from "./component";
import { IntrinsicComponentDefinition } from "./intrinsics/base";
import { input, output } from "./intrinsics/definitions";
Expand Down Expand Up @@ -45,10 +44,7 @@ export type CCComponentPinBitWidthStatus =
| { isFixed: false; fixMode: "automatic" | "nodeDependent" }
| { isFixed: true; bitWidth: number };

export type CCComponentPinStoreEvents = {
didRegister(pin: CCComponentPin): void;
willUnregister(pin: CCComponentPin): void;
didUnregister(pin: CCComponentPin): void;
export type CCComponentPinStoreEvents = CCBaseStoreEvents<CCComponentPin> & {
didUpdate(pin: CCComponentPin): void;
};
export const ccComponentPinStoreChangeEventTypes: (keyof CCComponentPinStoreEvents)[] =
Expand All @@ -57,40 +53,28 @@ export const ccComponentPinStoreChangeEventTypes: (keyof CCComponentPinStoreEven
/**
* Store of pins
*/
export class CCComponentPinStore extends EventEmitter<CCComponentPinStoreEvents> {
#store: CCStore;

#pins: Map<CCComponentPinId, CCComponentPin> = new Map();

/**
* Constructor of CCComponentPinStore
* @param store store
* @param pins initial pins
*/
constructor(store: CCStore) {
super();
this.#store = store;
}

export class CCComponentPinStore extends CCBaseStore<
CCComponentPinId,
CCComponentPin,
CCComponentPinStoreEvents
> {
import(componentPins: CCComponentPin[]): void {
for (const pin of componentPins) {
this.#pins.set(pin.id, pin);
this.objects.set(pin.id, pin);
}
}

mount() {
this.#store.nodePins.on("didRegister", (nodePin) => {
const componentPin = this.#store.componentPins.get(
nodePin.componentPinId,
);
this.store.nodePins.on("didRegister", (nodePin) => {
const componentPin = this.store.componentPins.get(nodePin.componentPinId);
if (
componentPin?.id === input.outputPin.Out.id ||
componentPin?.id === output.inputPin.In.id
) {
this.register(this.createForNodePin(nodePin.id));
}
});
this.#store.nodePins.on("willUnregister", (nodePin) => {
this.store.nodePins.on("willUnregister", (nodePin) => {
const pin = this.getByImplementation(nodePin.id);
if (pin) this.unregister(pin.id);
});
Expand All @@ -101,17 +85,17 @@ export class CCComponentPinStore extends EventEmitter<CCComponentPinStoreEvents>
* @param pin pin to be registered
*/
register(pin: CCComponentPin): void {
invariant(this.#store.components.get(pin.componentId));
this.#pins.set(pin.id, pin);
invariant(this.store.components.get(pin.componentId));
this.objects.set(pin.id, pin);
this.emit("didRegister", pin);
}

createForNodePin(nodePinId: CCNodePinId): CCComponentPin {
const targetNodePin = nullthrows(this.#store.nodePins.get(nodePinId));
const targetNodePin = nullthrows(this.store.nodePins.get(nodePinId));
const targetComponentPin = nullthrows(
this.#store.componentPins.get(targetNodePin.componentPinId),
this.store.componentPins.get(targetNodePin.componentPinId),
);
const targetNode = nullthrows(this.#store.nodes.get(targetNodePin.nodeId));
const targetNode = nullthrows(this.store.nodes.get(targetNodePin.nodeId));
const existingComponentPins = this.getManyByComponentId(
targetNode.parentComponentId,
);
Expand All @@ -133,29 +117,20 @@ export class CCComponentPinStore extends EventEmitter<CCComponentPinStoreEvents>
* @param id id of a pin to be unregistered
*/
async unregister(id: CCComponentPinId): Promise<void> {
const pin = nullthrows(this.#pins.get(id));
await this.#store.transactionManager.runInTransaction(() => {
const pin = nullthrows(this.objects.get(id));
await this.store.transactionManager.runInTransaction(() => {
this.emit("willUnregister", pin);
this.#pins.delete(id);
this.objects.delete(id);
});
this.emit("didUnregister", pin);
}

/**
* Get a pin by id
* @param id id of pin
* @returns pin of `id`
*/
get(id: CCComponentPinId): CCComponentPin | undefined {
return this.#pins.get(id);
}

/**
* Get all of pins
* @returns all pins
*/
getByNodePinId(nodePinId: CCNodePinId): CCComponentPin {
const pin = [...this.#pins.values()].find(
const pin = [...this.objects.values()].find(
({ implementation }) => implementation && implementation === nodePinId,
);
invariant(pin);
Expand All @@ -168,14 +143,14 @@ export class CCComponentPinStore extends EventEmitter<CCComponentPinStoreEvents>
* @returns pins of component
*/
getManyByComponentId(componentId: CCComponentId): CCComponentPin[] {
return [...this.#pins.values()].filter(
return [...this.objects.values()].filter(
(pin) => pin.componentId === componentId,
);
}

getByImplementation(implementation: CCNodePinId): CCComponentPin | null {
return (
[...this.#pins.values()].find(
[...this.objects.values()].find(
(pin) => pin.implementation === implementation,
) ?? null
);
Expand All @@ -190,9 +165,9 @@ export class CCComponentPinStore extends EventEmitter<CCComponentPinStoreEvents>
id: CCComponentPinId,
value: Partial<Pick<CCComponentPin, "name">>,
): void {
const pin = this.#pins.get(id);
const pin = this.objects.get(id);
invariant(pin);
this.#pins.set(id, { ...pin, ...value });
this.objects.set(id, { ...pin, ...value });
this.emit("didUpdate", pin);
}

Expand All @@ -206,7 +181,7 @@ export class CCComponentPinStore extends EventEmitter<CCComponentPinStoreEvents>
getComponentPinBitWidthStatus(
pinId: CCComponentPinId,
): CCComponentPinBitWidthStatus {
const pin = this.#pins.get(pinId);
const pin = this.objects.get(pinId);
invariant(pin);

// Intrinsic components
Expand All @@ -233,7 +208,7 @@ export class CCComponentPinStore extends EventEmitter<CCComponentPinStoreEvents>
pin.implementation,
"Pin implementation must be defined for user-defined components",
);
const bitWidthStatus = this.#store.nodePins.getNodePinBitWidthStatus(
const bitWidthStatus = this.store.nodePins.getNodePinBitWidthStatus(
pin.implementation,
);
if (bitWidthStatus.isFixed) {
Expand All @@ -254,12 +229,4 @@ export class CCComponentPinStore extends EventEmitter<CCComponentPinStoreEvents>
...partialPin,
};
}

/**
* Get array of pins
* @returns array of pins
*/
getMany(): CCComponentPin[] {
return [...this.#pins.values()];
}
}
Loading
Loading