Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
213fb30
store updater api
jbolda Sep 25, 2025
fbfce31
beginning of yjs example
jbolda Sep 25, 2025
7883a04
lint and fix command
jbolda Sep 25, 2025
f523c98
fmt again?
jbolda Sep 25, 2025
109dbf2
Merge branch 'main' into custom-store-updater
jbolda Nov 1, 2025
2fecfdd
use node 20 for publish
jbolda Nov 1, 2025
ab499db
Merge branch 'main' into custom-store-updater
jbolda Nov 5, 2025
f415a2a
try re-exporting full effection
jbolda Nov 15, 2025
0ad91fe
Merge branch 'main' into custom-store-updater
jbolda Nov 15, 2025
8471bf1
lint
jbolda Nov 15, 2025
674fad0
lift signals
jbolda Nov 18, 2025
806a08d
initialize is sequential
jbolda Nov 18, 2025
ef94711
pass scope to initialize
jbolda Nov 18, 2025
369d267
Merge branch 'main' into custom-store-updater
jbolda Dec 24, 2025
a9b0abd
store.manage()
jbolda Jan 3, 2026
86f4e94
spike out createSchema 'holding' the storage
jbolda Jan 23, 2026
fd7f968
Merge branch 'main' into custom-store-updater
jbolda Jan 24, 2026
04d709b
type test
jbolda Feb 1, 2026
781b139
full type refactor removing low value generics and overloads
jbolda Feb 24, 2026
b0daef9
Merge branch 'main' into custom-store-updater
jbolda Feb 25, 2026
01d1986
merge states, run initialize through resource loop
jbolda Feb 27, 2026
7b6c641
pass initialize through schema
jbolda Feb 27, 2026
a642825
move up watch init
jbolda Feb 27, 2026
550f31a
refine loader and react types
jbolda Apr 10, 2026
a5a7f74
tasks arg and manage helper
jbolda Apr 10, 2026
841e402
Merge branch 'main' into custom-store-updater
jbolda Apr 10, 2026
0e5a0f2
types massaging
jbolda Apr 13, 2026
5d7fe3f
add started to parallel
jbolda Apr 14, 2026
d13d5a5
examples and lints
jbolda Apr 14, 2026
962022b
switch to vitest in rtl test example
jbolda Apr 14, 2026
e119961
remove resource from direct store integration
jbolda Apr 14, 2026
cbbff1c
lint
jbolda Apr 14, 2026
190e536
remove vitest example alias
jbolda Apr 14, 2026
5d6918a
dedupe
jbolda Apr 14, 2026
82a5cd4
store runs produce
jbolda Apr 16, 2026
a54b1bd
fix yjs example
jbolda Apr 17, 2026
e7f7581
multi-schema as keyed object
jbolda May 6, 2026
cef07db
linted
jbolda May 6, 2026
f1c98cf
patience tested
jbolda May 6, 2026
6e59366
Provider handles multi-schema store
jbolda May 9, 2026
83fcfb9
cache helper, rework base schema slice type
jbolda May 15, 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
2 changes: 2 additions & 0 deletions .github/workflows/examples.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@ jobs:
fail-fast: false
matrix:
example:
- ./examples/basic
- ./examples/vite-react
- ./examples/parcel-react
- ./examples/tests-rtl
- ./examples/yjs

steps:
- name: checkout
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ docs/public/*
!docs/public/.gitkeep
dist/
src/package.json
.playwright*
4 changes: 2 additions & 2 deletions biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
"rules": {
"recommended": true,
"suspicious": {
"noExplicitAny": "off",
"noImplicitAnyLet": "off",
"noExplicitAny": "warn",
"noImplicitAnyLet": "warn",
"noConfusingVoidType": "off"
},
"correctness": {
Expand Down
2 changes: 1 addition & 1 deletion docs/posts/endpoints.md
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ function deserializeComment(com: any): Comment {
}

const [schema, initialState] = createSchema({
cache: slice.table(),
cache: slice.cache(),
loaders: slice.loaders(),
token: slice.str(),
articles: slice.table<Article>(),
Expand Down
2 changes: 1 addition & 1 deletion docs/posts/schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ a place for `starfx` and third-party functionality to hold their state.
import { createSchema, slice } from "starfx";

const [schema, initialState] = createSchema({
cache: slice.table(),
cache: slice.cache(),
loaders: slice.loaders(),
});
```
Expand Down
2 changes: 1 addition & 1 deletion docs/posts/store.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ interface User {

// app-wide database for ui, api data, or anything that needs reactivity
const [schema, initialState] = createSchema({
cache: slice.table(),
cache: slice.cache(),
loaders: slice.loaders(),
users: slice.table<User>(),
});
Expand Down
32 changes: 18 additions & 14 deletions examples/basic/src/main.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
import ReactDOM from "react-dom/client";
import { createApi, createSchema, createStore, mdw, timer } from "starfx";
import {
createApi,
createSchema,
createStore,
mdw,
slice,
timer,
} from "starfx";
import { Provider, useCache } from "starfx/react";

const [schema, initialState] = createSchema();
const store = createStore({ initialState });

const api = createApi();
const schema = createSchema({
cache: slice.table(),
loaders: slice.loaders(),
});
const store = createStore({ schema, tasks: [api.register] });

// mdw = middleware
api.use(mdw.api({ schema }));
api.use(api.routes());
Expand All @@ -14,17 +24,11 @@ api.use(mdw.fetch({ baseUrl: "https://api.github.com" }));
const fetchRepo = api.get(
"/repos/neurosnap/starfx",
{ supervisor: timer() },
api.cache(),
api.cache()
);

store.run(api.register);

function App() {
return (
<Provider schema={schema} store={store}>
<Example />
</Provider>
);
return <Example />;
}

function Example() {
Expand All @@ -47,7 +51,7 @@ function Example() {

const root = document.getElementById("root") as HTMLElement;
ReactDOM.createRoot(root).render(
<Provider schema={schema} store={store}>
<Provider store={store}>
<App />
</Provider>,
</Provider>
);
7 changes: 7 additions & 0 deletions examples/basic/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
{
"compilerOptions": {
"baseUrl": ".",
"forceConsistentCasingInFileNames": true,
"ignoreDeprecations": "5.0",
"target": "ESNext",
"lib": ["DOM", "DOM.Iterable", "ESNext"],
"module": "ESNext",
Expand All @@ -12,6 +15,10 @@
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
"paths": {
"starfx": ["../../src/index.ts"],
"starfx/react": ["../../src/react.ts"]
},

/* Linting */
"strict": true,
Expand Down
7 changes: 0 additions & 7 deletions examples/tests-rtl/babel.config.js

This file was deleted.

11 changes: 0 additions & 11 deletions examples/tests-rtl/jest.config.js

This file was deleted.

14 changes: 6 additions & 8 deletions examples/tests-rtl/package.json
Original file line number Diff line number Diff line change
@@ -1,23 +1,21 @@
{
"name": "tests-rtl",
"scripts": {
"test": "jest"
"test": "vitest run"
},
"dependencies": {
"starfx": "file:../.."
},
"devDependencies": {
"@babel/core": "^7.28.0",
"@babel/preset-env": "^7.28.0",
"@babel/preset-react": "^7.27.1",
"@babel/preset-typescript": "^7.27.1",
"@testing-library/dom": "^10.4.0",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.3.0",
"@testing-library/user-event": "^14.6.1",
"babel-jest": "^30.0.4",
"jest": "^30.0.4",
"jest-environment-jsdom": "^30.0.4",
"jsdom": "^26.1.0",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"react-redux": "^9.1.2",
"vitest": "^4.0.7",
"whatwg-fetch": "^3.6.20"
}
}
30 changes: 17 additions & 13 deletions examples/tests-rtl/src/api.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { createApi, createSchema, mdw, slice } from "starfx";
import { createTypedHooks } from "starfx/react";

const emptyUser = { id: "", name: "" };
export const [schema, initialState] = createSchema({
type User = typeof emptyUser;
export const schema = createSchema({
users: slice.table({ empty: emptyUser }),
cache: slice.table(),
loaders: slice.loaders(),
Expand All @@ -12,20 +14,22 @@ api.use(mdw.api({ schema }));
api.use(api.routes());
api.use(mdw.fetch({ baseUrl: "https://jsonplaceholder.typicode.com" }));

export const fetchUsers = api.get(
"/users",
function* (ctx, next) {
yield* next();
export const fetchUsers = api.get("/users", function* (ctx, next) {
yield* next();

if (!ctx.json.ok) {
return;
}
if (!ctx.json.ok) {
return;
}

const users = ctx.json.value.reduce((acc, user) => {
const users = (ctx.json.value as User[]).reduce<Record<string, User>>(
(acc, user) => {
acc[user.id] = user;
return acc;
}, {});
},
{},
);

yield* schema.update(schema.users.add(users));
},
);
yield* schema.update(schema.users.add(users));
});
const { useSelector } = createTypedHooks(schema);
export { useSelector };
6 changes: 3 additions & 3 deletions examples/tests-rtl/src/app.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useDispatch, useSelector } from "starfx/react";
import { fetchUsers, schema } from "./api";
import { useDispatch } from "starfx/react";
import { fetchUsers, schema, useSelector } from "./api";

export function App({ id }) {
export function App({ id }: { id: string }) {
const dispatch = useDispatch();
const user = useSelector((s) => schema.users.selectById(s, { id }));
const userList = useSelector(schema.users.selectTableAsList);
Expand Down
13 changes: 5 additions & 8 deletions examples/tests-rtl/src/store.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
import { api, initialState as schemaInitialState } from "./api";
import { api, schema } from "./api";
import { createStore } from "starfx";

export function setupStore({ initialState = {} }) {
export function setupStore({ initialState = {} } = {}) {
void initialState;
const store = createStore({
initialState: {
...schemaInitialState,
...initialState,
},
schema,
tasks: [api.register],
});

store.run(api.register);

return store;
}
4 changes: 1 addition & 3 deletions examples/tests-rtl/tests/app.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import React from "react";
import "@testing-library/jest-dom";
import { expect, test } from "@jest/globals";
import { expect, test } from "vitest";
import { fireEvent, render, screen, waitFor } from "./utils";
import { fetchUsers } from "../src/api";
import { App } from "../src/app";
Expand Down
8 changes: 7 additions & 1 deletion examples/tests-rtl/tests/setup.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import "@testing-library/jest-dom";
import "@testing-library/jest-dom/vitest";
import { cleanup } from "@testing-library/react";
import { afterEach } from "vitest";
// jsdom doesn't have the fetch API which we need for Response()
// so polyfilling it here for every file
// see https://github.com/jsdom/jsdom/issues/1724
import "whatwg-fetch";

afterEach(() => {
cleanup();
});
7 changes: 1 addition & 6 deletions examples/tests-rtl/tests/utils.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
import React, { type ReactElement } from "react";
import { render, type RenderOptions } from "@testing-library/react";
import { Provider } from "starfx/react";
import { schema } from "../src/api";
import { setupStore } from "../src/store";

const AllTheProviders = ({ children }: { children: React.ReactNode }) => {
const store = setupStore({});
return (
<Provider schema={schema} store={store}>
{children}
</Provider>
);
return <Provider store={store}>{children}</Provider>;
};

const customRender = (
Expand Down
10 changes: 7 additions & 3 deletions examples/tests-rtl/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,26 @@
"$schema": "https://json.schemastore.org/tsconfig",
"compilerOptions": {
"baseUrl": ".",
"types": ["vitest/globals"],
"forceConsistentCasingInFileNames": true,
"ignoreDeprecations": "5.0",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"target": "esnext",
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"noUnusedLocals": true,
"jsx": "react-jsx"
"jsx": "react-jsx",
"paths": {
"starfx": ["../../src/index.ts"],
"starfx/react": ["../../src/react.ts"]
}
},
"include": ["./src"]
}
11 changes: 11 additions & 0 deletions examples/tests-rtl/vitest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { defineConfig } from "vitest/config";

export default defineConfig({
resolve: {
dedupe: ["react", "react-dom", "react-redux"],
},
test: {
environment: "jsdom",
setupFiles: ["./tests/setup.ts"],
},
});
6 changes: 1 addition & 5 deletions examples/vite-react/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
import {
TypedUseSelectorHook,
useDispatch,
useSelector as useSel,
} from "starfx/react";
import "./App.css";
import { AppState, fetchUsers, schema } from "./api.ts";

const useSelector: TypedUseSelectorHook<AppState> = useSel;
import { fetchUsers, schema, useSelector } from "./api.ts";

function App({ id }: { id: string }) {
const dispatch = useDispatch();
Expand Down
3 changes: 3 additions & 0 deletions examples/vite-react/src/age-guess.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { type Operation, resource } from "effection";
import { registerResource } from "starfx";

export function guessAge(): Operation<{ guess: number; accumulated: number }> {
console.log("started");
Expand All @@ -25,3 +26,5 @@ export function guessAge(): Operation<{ guess: number; accumulated: number }> {
}
});
}

export const GlobalGuesser = registerResource("global-guesser", guessAge());
7 changes: 5 additions & 2 deletions examples/vite-react/src/api.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { createApi, createSchema, mdw, slice } from "starfx";
import { guessAge } from "./age-guess";
import { createTypedHooks } from "starfx/react";

interface User {
id: string;
Expand All @@ -8,12 +9,11 @@ interface User {
}

const emptyUser: User = { id: "", name: "", age: 0 };
export const [schema, initialState] = createSchema({
export const schema = createSchema({
users: slice.table({ empty: emptyUser }),
cache: slice.table(),
loaders: slice.loaders(),
});
export type AppState = typeof initialState;

export const api = createApi();
api.use(mdw.api({ schema }));
Expand Down Expand Up @@ -49,3 +49,6 @@ export const fetchUsers = api.get<never, Omit<User, "age">[]>(
yield* schema.update(schema.users.add(users));
}
);

export const { useSelector } = createTypedHooks(schema);

Loading
Loading