Skip to content

Commit 3180d9a

Browse files
committed
refactor: replace @thread decorator with ensemble.json configuration
Replace the @thread decorator with a declarative ensemble.json configuration file for defining threading topology. This provides better visibility, tooling support, and clearer project structure. Changes: - Remove @thread decorator and related metadata functions - Add ensemble.json configuration with actor-to-thread mappings - Create config loader with validation for ensemble.json - Add JSON schema for IDE autocomplete and validation - Update ActorSystem to use config-based thread assignment - Update Vite plugin to generate worker bundles from config - Separate dev/build bundling strategies in Vite plugin - Add on-demand worker bundling for dev mode via middleware Breaking changes: - @thread decorator no longer supported - Projects must create ensemble.json to define worker threads - ActorSystem constructor now accepts optional EnsembleConfig
1 parent 7e4e2fb commit 3180d9a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+871
-340
lines changed

README.md

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,19 @@ npm install @d-buckner/ensemble-collaboration
6868
```typescript
6969
import { createActorToken, ActorSystem, Actor, action } from '@d-buckner/ensemble-core';
7070

71-
// 1. Define an actor
72-
class CounterActor extends Actor {
71+
// 1. Define actor interfaces
72+
interface CounterState {
73+
count: number;
74+
}
75+
76+
interface CounterActions {
77+
increment(): void;
78+
}
79+
80+
interface CounterEvents {}
81+
82+
// 2. Define an actor
83+
class CounterActor extends Actor<CounterState, CounterActions, CounterEvents> {
7384
state = { count: 0 };
7485

7586
@action
@@ -78,14 +89,14 @@ class CounterActor extends Actor {
7889
}
7990
}
8091

81-
// 2. Create a token and system
92+
// 3. Create a token and system
8293
const CounterToken = createActorToken<CounterActor>('counter');
8394
const system = new ActorSystem();
8495

8596
system.register({ token: CounterToken, actor: CounterActor });
8697
await system.start();
8798

88-
// 3. Use the actor
99+
// 4. Use the actor
89100
const counter = system.get(CounterToken);
90101
counter.on('stateChanged', (state) => console.log(state));
91102
counter.actions.increment(); // { count: 1 }

demos/react/collaboration/src/actors/TodosActor.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,15 @@ export interface TodoDoc {
66
todos: Array<{ id: string; text: string; done: boolean }>;
77
}
88

9+
// Define actions
10+
export interface TodosActions {
11+
addTodo(text: string): void;
12+
toggleTodo(id: string): void;
13+
removeTodo(id: string): void;
14+
}
15+
916
// Extend CollaborationActor with domain actions
10-
export class TodosActor extends CollaborationActor<TodoDoc> {
17+
export class TodosActor extends CollaborationActor<TodoDoc, TodosActions> {
1118
protected declare deps: CollaborationDeps;
1219
static readonly initialState: TodoDoc = {
1320
todos: []

demos/react/counter/ensemble.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"$schema": "../../../ensemble.schema.json",
3+
"threads": {
4+
"counter": {
5+
"actors": [
6+
{
7+
"path": "./src/actors/CounterActor.ts",
8+
"name": "CounterActor"
9+
}
10+
]
11+
}
12+
}
13+
}

demos/react/counter/src/actors/CounterActor.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
1-
import { Actor, action, thread } from '@d-buckner/ensemble-core';
1+
import { Actor, action } from '@d-buckner/ensemble-core';
22

33

44
export interface CounterState {
55
count: number;
66
}
77

8-
@thread('counter')
9-
export class CounterActor extends Actor<CounterState> {
8+
export interface CounterActions {
9+
increment(): void;
10+
decrement(): void;
11+
reset(): void;
12+
}
13+
14+
export class CounterActor extends Actor<CounterState, CounterActions> {
1015
static readonly initialState: CounterState = { count: 0 };
1116

1217
constructor() {

demos/react/counter/src/index.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@ import { createRoot } from 'react-dom/client';
55
import { CounterActor } from './actors/CounterActor';
66
import { App } from './App';
77
import { CounterToken } from './tokens';
8+
import ensembleConfig from '../ensemble.json';
89

910

1011
async function main() {
11-
const system = new ActorSystem();
12+
// Load thread configuration from ensemble.json
13+
const system = new ActorSystem(ensembleConfig);
1214

1315
system.register({
1416
token: CounterToken,

demos/solidjs/counter/src/actors/CounterActor.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,13 @@ export interface CounterState extends Record<string, unknown> {
55
count: number;
66
}
77

8-
export class CounterActor extends Actor<CounterState> {
8+
export interface CounterActions {
9+
increment(): void;
10+
decrement(): void;
11+
reset(): void;
12+
}
13+
14+
export class CounterActor extends Actor<CounterState, CounterActions> {
915
static readonly initialState: CounterState = { count: 0 };
1016

1117
constructor() {
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"$schema": "../../../ensemble.schema.json",
3+
"threads": {
4+
"worker-1": {
5+
"actors": [
6+
{
7+
"path": "./src/actors/MetricGeneratorActor.ts",
8+
"name": "MetricGeneratorActor"
9+
},
10+
{
11+
"path": "./src/actors/StatisticsActor.ts",
12+
"name": "StatisticsActor"
13+
}
14+
]
15+
},
16+
"worker-2": {
17+
"actors": [
18+
{
19+
"path": "./src/actors/AnomalyDetectionActor.ts",
20+
"name": "AnomalyDetectionActor"
21+
}
22+
]
23+
}
24+
}
25+
}

demos/solidjs/metrics-dashboard/src/actors/AnomalyDetectionActor.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Actor, effect, thread } from '@d-buckner/ensemble-core';
1+
import { Actor, effect } from '@d-buckner/ensemble-core';
22
import type { StatisticsActor, ProcessedBatch, ProcessedMetrics } from './StatisticsActor';
33
import type { IActorClient } from '@d-buckner/ensemble-core';
44

@@ -18,6 +18,8 @@ export interface AnomalyDetectionState {
1818
recentAnomalies: Anomaly[];
1919
}
2020

21+
export interface AnomalyDetectionActions {}
22+
2123
export interface AnomalyDetectionEvents {
2224
latestAnomaly: Anomaly;
2325
}
@@ -35,8 +37,7 @@ interface AnomalyDetectionDeps {
3537
* - Historical state tracking for trend detection
3638
* - Reactive event communication
3739
*/
38-
@thread('worker-2')
39-
export class AnomalyDetectionActor extends Actor<AnomalyDetectionState, AnomalyDetectionEvents> {
40+
export class AnomalyDetectionActor extends Actor<AnomalyDetectionState, AnomalyDetectionActions, AnomalyDetectionEvents> {
4041
static readonly initialState: AnomalyDetectionState = {
4142
isMonitoring: false,
4243
anomaliesDetected: 0,

demos/solidjs/metrics-dashboard/src/actors/DashboardActor.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ export interface DashboardState {
2424
windowSize: number; // Number of data points to keep
2525
}
2626

27+
export interface DashboardActions {}
28+
2729
interface DashboardDeps {
2830
StatisticsActor: ActorClient<StatisticsActor>;
2931
AnomalyDetectionActor: ActorClient<AnomalyDetectionActor>;
@@ -40,7 +42,7 @@ interface DashboardDeps {
4042
*
4143
* Runs on MAIN THREAD for direct UI access
4244
*/
43-
export class DashboardActor extends Actor<DashboardState> {
45+
export class DashboardActor extends Actor<DashboardState, DashboardActions> {
4446
static readonly initialState: DashboardState = {
4547
chartData: {
4648
cpuSeries: [],

demos/solidjs/metrics-dashboard/src/actors/MetricGeneratorActor.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Actor, action, thread } from '@d-buckner/ensemble-core';
1+
import { Actor, action } from '@d-buckner/ensemble-core';
22
import { createNoise2D } from 'simplex-noise';
33

44

@@ -46,6 +46,14 @@ export interface MetricGeneratorState {
4646
throughput: number; // target metrics per second
4747
}
4848

49+
export interface MetricGeneratorActions {
50+
start(): void;
51+
stop(): void;
52+
setBatchSize(size: number): void;
53+
setThroughput(throughput: number): void;
54+
setPaused(paused: boolean): void;
55+
}
56+
4957
export interface MetricGeneratorEvents {
5058
metricBatch: MetricBatch;
5159
}
@@ -58,8 +66,7 @@ export interface MetricGeneratorEvents {
5866
* - Complex data structure generation
5967
* - Continuous streaming
6068
*/
61-
@thread('worker-1')
62-
export class MetricGeneratorActor extends Actor<MetricGeneratorState, MetricGeneratorEvents> {
69+
export class MetricGeneratorActor extends Actor<MetricGeneratorState, MetricGeneratorActions, MetricGeneratorEvents> {
6370
static readonly initialState: MetricGeneratorState = {
6471
isGenerating: false,
6572
batchesGenerated: 0,

0 commit comments

Comments
 (0)