Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
a29bcc0
WIP
cowboyd Dec 12, 2025
28f0889
mini readme
jbolda Dec 30, 2025
ea39dc3
export functions
jbolda Dec 31, 2025
79f39a5
add small tests
jbolda Jan 5, 2026
b831737
export context impl only
jbolda Jan 5, 2026
b2cc21e
spike out polling watch
jbolda Jan 7, 2026
f322e46
combine read/write and implement context set
cowboyd Jan 7, 2026
0d7777a
add streaming context updates
cowboyd Jan 7, 2026
6098b65
map preview package of effection with middleware
cowboyd Jan 8, 2026
d7a0aa8
new version of context inspector based on scope api
cowboyd Jan 8, 2026
84a46ad
Add based on scope api
cowboyd Jan 10, 2026
4b39ddb
Use Task decorator
cowboyd Jan 10, 2026
6adb968
focus on task events
cowboyd Jan 13, 2026
56e8662
use global.decorate instead of global.eval
cowboyd Jan 19, 2026
19d258b
fix tests and biome lints
jbolda Jan 19, 2026
e7d98ab
spike out UI
jbolda Jan 19, 2026
f8781ff
ui slider for tick
jbolda Jan 20, 2026
e0a5d61
base events on Scope API
cowboyd Jan 21, 2026
2f08944
fix imports
jbolda Jan 21, 2026
6c51cd1
simple SSE at :41000 at root
jbolda Jan 21, 2026
ade2dea
send labels
cowboyd Jan 21, 2026
9e042b3
load stuff
cowboyd Jan 21, 2026
99e3b8f
pipe data to store, render ticks
jbolda Jan 21, 2026
7356ddc
Add a general client for the SSE transport
cowboyd Jan 22, 2026
117fc88
Add RPC capabilities
cowboyd Jan 22, 2026
b53f7e8
Add getScopes() to read the whole tree
cowboyd Jan 22, 2026
65d1e20
Include the parentId when getting all scopes
cowboyd Jan 22, 2026
8136e53
fix sseclient and further wiring
jbolda Jan 23, 2026
6ee0366
setup a pipeline test
cowboyd Jan 23, 2026
4918e7b
checkpoint
cowboyd Jan 24, 2026
a4fa61a
move transformers into ui/data
cowboyd Jan 24, 2026
06daf93
Add recording API
cowboyd Jan 26, 2026
eb7540c
direct react pipeline spike
jbolda Jan 26, 2026
0d55eec
rendering a tree spike
jbolda Jan 26, 2026
18e955c
make some changes and stuff
cowboyd Jan 27, 2026
fb130e7
further develop UI
jbolda Jan 28, 2026
448d12f
better tree labels
jbolda Jan 28, 2026
c68c949
Make entire inspector run directly off the global scope
cowboyd Jan 28, 2026
59d70fc
Add more comprehensive tree
cowboyd Jan 29, 2026
d0a8d40
Make labels really, really simple
cowboyd Jan 29, 2026
0a3142c
export to PNG spike
jbolda Jan 29, 2026
8ab022a
Always assume that we will have a root node
cowboyd Jan 29, 2026
fe9fe69
react-router and top-level app spike
jbolda Jan 29, 2026
7817117
wrap node selection into url params
jbolda Jan 30, 2026
28d16d6
lint and fmt
jbolda Jan 30, 2026
7221412
finish linting
cowboyd Jan 30, 2026
076b0a7
Merge branch 'main' into inspector
jbolda Jan 30, 2026
9ebc642
bundle up vite app
jbolda Jan 30, 2026
1c4da23
Connect to live process
cowboyd Jan 30, 2026
93a16f3
remove console.dir
cowboyd Jan 30, 2026
8f74836
static serve app
jbolda Jan 30, 2026
5ad24fd
rename Pump events
cowboyd Jan 30, 2026
3a37b6c
try to package things up for better inspection
cowboyd Jan 30, 2026
b877ba3
use pnpm pack for preview packages
jbolda Jan 30, 2026
ee4f839
try with pnpx
jbolda Jan 30, 2026
a1b4aec
echo dirs
jbolda Jan 30, 2026
67ec2b9
with pnpm flag
jbolda Jan 30, 2026
e32585b
without loop
jbolda Jan 30, 2026
ff51603
npx but flag
jbolda Jan 30, 2026
a28e5c7
remove comment flag
jbolda Jan 30, 2026
48956b2
fix static path for dist
jbolda Jan 30, 2026
d8e35f6
spike using style macro
jbolda Feb 3, 2026
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
3 changes: 2 additions & 1 deletion .github/workflows/preview.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,5 @@ jobs:
for workspace in ${{ steps.changed.outputs.workspaces }}; do
dirs="$dirs ./$workspace"
done
npx pkg-pr-new publish $dirs
echo $dirs
npx pkg-pr-new publish --pnpm $dirs
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,4 @@ dist/

# pnpm config (created by test-matrix runner)
.npmrc

.turbo
3 changes: 2 additions & 1 deletion biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
"recommended": true,
"style": {
"noParameterAssign": "off",
"useConst": "off"
"useConst": "off",
"noUselessElse": "off"
},
"correctness": {
"useYield": "off"
Expand Down
3 changes: 3 additions & 0 deletions inspector/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# inspector

Helpers to inspect an effection tree.
23 changes: 23 additions & 0 deletions inspector/examples/example.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { main, sleep, spawn, type Task } from "effection";

import { useLabels } from ".././lib/labels.ts";

await main(function* () {
let tasks: Task<string>[] = [];
for (let i = 1; i <= 10; i++) {
let task = yield* spawn(function* () {
yield* useLabels({ name: "child", number: i });
let delay = Math.random() * 1000;
yield* sleep(delay);
return `${i} is done`;
});
tasks.push(task);
}

for (let task of tasks) {
yield* sleep(Math.random() * 200);
yield* task.halt();
}

console.log("done");
});
12 changes: 12 additions & 0 deletions inspector/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>vite + react + starfx</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/ui/main.tsx"></script>
</body>
</html>
25 changes: 25 additions & 0 deletions inspector/lib/attach.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { type Operation, type Scope, suspend, withResolvers } from "effection";
import type { Methods, Inspector, Handle } from "./types.ts";
import { useLabels } from "./labels.ts";

export function* attach<M extends Methods>(
scope: Scope,
inspector: Inspector<M>,
init: (handle: Handle<M>) => Operation<void>,
): Operation<void> {
let attached = withResolvers<void>();

scope.run(function* () {
yield* useLabels({ name: "Inspector" });
try {
let handle = yield* inspector.attach(scope);
yield* init(handle);
attached.resolve();
yield* suspend();
} catch (error) {
attached.reject(error as Error);
}
});

return yield* attached.operation;
}
8 changes: 8 additions & 0 deletions inspector/lib/box.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type { Result } from "effection";

export function unbox<T>(result: Result<T>): T {
if (result.ok) {
return result.value;
}
throw result.error;
}
66 changes: 66 additions & 0 deletions inspector/lib/combine.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import type { Operation, Scope } from "effection";
import type { Handle, Inspector, Methods, Protocol } from "./types.ts";

export interface Combine {
protocols<A extends Methods>(...protocols: [Protocol<A>]): Protocol<A>;
protocols<A extends Methods, B extends Methods>(
...protocols: [Protocol<A>, Protocol<B>]
): Protocol<A & B>;
protocols<A extends Methods, B extends Methods, C extends Methods>(
...protocols: [Protocol<A>, Protocol<B>, Protocol<C>]
): Protocol<A & B & C>;
protocols<
A extends Methods,
B extends Methods,
C extends Methods,
D extends Methods,
>(
...protocols: [Protocol<A>, Protocol<B>, Protocol<C>, Protocol<D>]
): Protocol<A & B & C & D>;

inspectors<A extends Methods>(...inspectors: [Inspector<A>]): Inspector<A>;
inspectors<A extends Methods, B extends Methods>(
...inspectors: [Inspector<A>, Inspector<B>]
): Inspector<A & B>;
inspectors<A extends Methods, B extends Methods, C extends Methods>(
...inspectors: [Inspector<A>, Inspector<B>, Inspector<C>]
): Inspector<A & B & C>;
inspectors<
A extends Methods,
B extends Methods,
C extends Methods,
D extends Methods,
>(
...inspectors: [Inspector<A>, Inspector<B>, Inspector<C>, Inspector<D>]
): Inspector<A & B & C & D>;
}

export const combine: Combine = {
protocols: (...protocols: Protocol<Methods>[]) => {
return protocols.reduce(
(acc, protocol) => {
Object.assign(acc.methods, protocol.methods);
return acc;
},
{ methods: {} } as Protocol<Methods>,
);
},
inspectors: (...inspectors: Inspector<Methods>[]) => {
return inspectors.reduce(
(acc: Inspector<Methods>, inspector: Inspector<Methods>) => {
let protocol = combine.protocols(acc.protocol, inspector.protocol);
let attach = function* (scope: Scope): Operation<Handle<Methods>> {
let a = yield* acc.attach(scope);
let b = yield* inspector.attach(scope);
let methods = Object.assign({}, a.methods, b.methods);
return {
protocol,
methods,
invoke: ({ name, args }) => methods[name](...args),
};
};
return { protocol, attach };
},
);
},
};
26 changes: 26 additions & 0 deletions inspector/lib/impl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { lift, type Operation, type Stream } from "effection";

export function fn<TArgs extends unknown[], TReturn>(
fn: (...args: TArgs) => TReturn,
): (...args: TArgs) => Stream<never, TReturn> {
return lift((...args) => ({
*next() {
return { done: true, value: fn(...args) };
},
}));
}

export function op<TArgs extends unknown[], TReturn>(
fn: (...args: TArgs) => Operation<TReturn>,
): (
...args: TArgs
) => Stream<never, TReturn extends void ? undefined : TReturn> {
return lift((...args) => ({
*next() {
return {
done: true,
value: yield* fn(...args),
} as IteratorReturnResult<TReturn extends void ? undefined : TReturn>;
},
}));
}
25 changes: 25 additions & 0 deletions inspector/lib/implementation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import type { Operation, Scope } from "effection";
import type {
Handle,
Implementation,
Inspector,
Methods,
Protocol,
} from "./types.ts";

export function createImplementation<M extends Methods>(
protocol: Protocol<M>,
create: Implementation<M>,
): Inspector<M> {
return {
protocol,
*attach(scope: Scope): Operation<Handle<M>> {
let methods = yield* create(scope);
return {
protocol,
methods,
invoke: ({ name, args }) => methods[name](...args),
};
},
};
}
28 changes: 28 additions & 0 deletions inspector/lib/labels.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { createContext, type Scope, useScope, type Operation } from "effection";

export type Labels = Record<string, string | number | boolean>;

export const LabelsContext = createContext<Labels>(
"@effectionx/inspector.labels",
{ name: "anonymous" },
);

export function* useLabels(labels: Labels): Operation<Labels> {
let scope = yield* useScope();

if (!scope.hasOwn(LabelsContext)) {
return yield* LabelsContext.set({
...LabelsContext.defaultValue,
...labels,
});
}
let current = yield* LabelsContext.expect();
return yield* LabelsContext.set({ ...current, ...labels });
}

export function getLabels(scope: Scope) {
if (scope.hasOwn(LabelsContext)) {
return scope.expect(LabelsContext);
}
return LabelsContext.defaultValue as Labels;
}
5 changes: 5 additions & 0 deletions inspector/lib/mod.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export * from "./types.ts";
export * from "./protocol.ts";
export * from "./implementation.ts";
export * from "./combine.ts";
export * from "./reader.ts";
9 changes: 9 additions & 0 deletions inspector/lib/protocol.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type { Method, Protocol } from "./types.ts";

export function createProtocol<
M extends Record<string, Method<unknown[], unknown, unknown>>,
>(methods: M): Protocol<M> {
return {
methods,
};
}
7 changes: 7 additions & 0 deletions inspector/lib/reader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export function toJson(value: unknown) {
try {
return JSON.parse(JSON.stringify(value));
} catch (_error) {
return String(value);
}
}
47 changes: 47 additions & 0 deletions inspector/lib/reduce.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import type { Operation, Stream } from "effection";

/**
* Transforms a stream by taking each item and applying it to an
* accumulated value to produce a new accumulated value which is then
* passed downstream.
*
* @example
* ```ts
* import { reduce, streamOf } from "@effectionx/stream-helpers";
*
* const sum = reduce(function*(total: number, item: number) {
* return sum + number;
* }, 0);
*
* sum(streamOf([1,2,3])) //=> yields 1, 3, 6
* ```
*
* @param fn - The operation to apply a single item to the accumulated value
* @param initial - The first accumulated value from which all others will descend
* @returns A stream transformer that applies the reduction over the lifetime of the stream
*/
export function reduce<T, TSum>(
fn: (current: TSum, item: T) => Operation<TSum>,
initial: TSum,
): <TClose>(stream: Stream<T, TClose>) => Stream<TSum, TClose> {
return (upstream) => ({
*[Symbol.iterator]() {
let current = initial;
let subscription = yield* upstream;

return {
*next() {
let next = yield* subscription.next();
if (next.done) {
return next;
}
let reduction = yield* fn(current, next.value);

current = reduction;

return { done: false, value: current } as const;
},
};
},
});
}
Loading