Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Starfx and yjs #821

Draft
wants to merge 5 commits into
base: next
Choose a base branch
from
Draft
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
1,077 changes: 942 additions & 135 deletions package-lock.json

Large diffs are not rendered by default.

7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
"pretty": "prettier --write \"{src,__{tests,mocks}__}/**/*.js\"",
"playwright": "playwright",
"tauri": "tauri",
"covector": "covector"
"covector": "covector",
"postinstall": "patch-package"
},
"dependencies": {
"@dinero.js/currencies": "^2.0.0-alpha.14",
Expand All @@ -38,6 +39,8 @@
"react-router-dom": "^6.23.0",
"starfx": "0.13.0",
"tailwindcss-react-aria-components": "^1.1.1",
"yjs": "^13.6.19",
"y-webrtc": "^10.3.0",
"zod": "^3.23.8"
},
"overrides": {
Expand All @@ -60,8 +63,10 @@
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
"@types/big.js": "^6.2.2",
"@types/node": "^20.12.7",
"buffer": "^6.0.3",
"covector": "^0.12.0",
"parcel": "^2.11.0",
"patch-package": "8.0.0",
"postcss": "^8.4.32",
"prettier": "^3.2.5",
"process": "^0.11.10",
Expand Down
48 changes: 48 additions & 0 deletions patches/starfx+0.13.0.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
diff --git a/node_modules/starfx/esm/store/fx.js b/node_modules/starfx/esm/store/fx.js
index e19b823..d799983 100644
--- a/node_modules/starfx/esm/store/fx.js
+++ b/node_modules/starfx/esm/store/fx.js
@@ -1,11 +1,11 @@
import { StoreContext } from "./context.js";
import { parallel, safe } from "../fx/mod.js";
import { getIdFromAction, take } from "../action.js";
-export function* updateStore(updater) {
+export function* updateStore(updater, meta) {
const store = yield* StoreContext;
// had to cast the store since StoreContext has a generic store type
const st = store;
- const ctx = yield* st.update(updater);
+ const ctx = yield* st.update(updater, meta);
return ctx;
}
export function* select(selectorFn, p) {
diff --git a/node_modules/starfx/esm/store/schema.js b/node_modules/starfx/esm/store/schema.js
index a1f2d2c..819de01 100644
--- a/node_modules/starfx/esm/store/schema.js
+++ b/node_modules/starfx/esm/store/schema.js
@@ -14,8 +14,8 @@ export function createSchema(slices = defaultSchema()) {
acc[key] = db[key].initialState;
return acc;
}, {});
- function* update(ups) {
- return yield* updateStore(ups);
+ function* update(ups, meta) {
+ return yield* updateStore(ups, meta);
}
db.update = update;
return [db, initialState];
diff --git a/node_modules/starfx/esm/store/store.js b/node_modules/starfx/esm/store/store.js
index 427c8ac..49a7f61 100644
--- a/node_modules/starfx/esm/store/store.js
+++ b/node_modules/starfx/esm/store/store.js
@@ -85,8 +85,9 @@ export function createStore({ initialState, scope: initScope, middleware = [], }
return fn;
}
const mdw = createUpdater();
- function* update(updater) {
+ function* update(updater, meta) {
const ctx = {
+ meta,
updater,
patches: [],
result: Ok(undefined),
30 changes: 30 additions & 0 deletions src/pages/homepage/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
import React from 'react';
import { useDispatch, useSelector } from 'starfx/react';

import { schema } from '~/src/store/schema';
import { updater } from '~/src/store/thunks';

const Homepage = () => <Hero />;

export default Homepage;

function Hero() {
const dispatch = useDispatch();
const count = useSelector(schema.count.select);
const list = useSelector(schema.list.selectTableAsList);
return (
<div className="relative overflow-hidden">
<div
Expand Down Expand Up @@ -102,6 +109,29 @@ function Hero() {
information can be built upon going forward to forecast
considerations like FI(RE).
</p>
<button
onClick={() => dispatch(updater([schema.count.increment(1)]))}
>
Increase counter from {count}
</button>
<ul>
{list.map((l) => (
<li key={l.thing}>{l.thing}</li>
))}
</ul>
<button
onClick={() =>
dispatch(
updater([
schema.list.add({
[Date.now()]: { thing: Math.random().toString() }
})
])
)
}
>
Bump List
</button>
<div className="mt-5 max-w-md mx-auto sm:flex sm:justify-center md:mt-8">
<div className="rounded-md shadow">
<a
Expand Down
4 changes: 3 additions & 1 deletion src/store/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,9 @@ const [schema, initialState] = createSchema({
accounts: slice.table<Account>({ empty: emptyAccount }),
chartRange: slice.obj(defaultChartBarRange),
incomeReceived: slice.table<IncomeReceived>(),
incomeExpected: slice.table<IncomeExpected>()
incomeExpected: slice.table<IncomeExpected>(),
count: slice.num(),
list: slice.table<{ thing: string }>()
});

export { schema, initialState };
Expand Down
63 changes: 59 additions & 4 deletions src/store/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,33 @@ import {
parallel,
PERSIST_LOADER_ID,
persistStoreMdw,
take
take,
takeEvery
} from 'starfx';
import { WebrtcProvider } from 'y-webrtc';
import * as Y from 'yjs';

import {
AppState,
initialState as schemaInitialState,
schema
} from './schema.ts';
import { connectReduxDevToolsExtension } from './thunks/devtools.ts';
import { tasks, thunks } from './thunks/index.ts';
import { sync, tasks, thunks } from './thunks/index.ts';
import { reconcilerWithReconstitution } from './utils/reconcilerWithReconstitution.ts';
import { applyPatch, applyYEvent } from './yjs/index.ts';

const protocol = 'wss'; // window.location.protocol === 'https' ? 'wss' : 'ws';
const ydoc = new Y.Doc();
const provider = new WebrtcProvider('your-room-name', ydoc, {
signaling: [`${protocol}://demos.yjs.dev/ws`],
password: 'optional-room-password'
});
// array of numbers which produce a sum
const yarray = ydoc.getArray('count');
const ymap = ydoc.getMap<{ thing: string }>('list');

const yjsAllowlist: (keyof typeof schema)[] = ['count', 'list'];

const devtoolsEnabled = true;
export function setupStore({ logs = true, initialState = {} }) {
Expand All @@ -30,7 +46,8 @@ export function setupStore({ logs = true, initialState = {} }) {
'accounts',
'transactions',
'incomeReceived',
'incomeExpected'
'incomeExpected',
'list'
]
});

Expand All @@ -48,10 +65,48 @@ export function setupStore({ logs = true, initialState = {} }) {
tsks.push(function* logActions() {
while (true) {
const action = yield* take('*');
console.log(action);
console.log('everyActionLogger', action);
}
});
}

tsks.push(function* syncWithYjs() {
for (let allowedTypeKey of yjsAllowlist) {
const allowedType = ydoc.share.get(allowedTypeKey);
if (!allowedType)
throw new Error(`${allowedTypeKey} is not part of ydoc`);
allowedType.observe((event) => {
if (!event.transaction.local) {
store.dispatch(
sync([
schema.list.set(
// @ts-expect-error type index issue but :shrug:
applyYEvent(store.getState()[allowedTypeKey], event)
)
])
);
}
});
}

// sync immerjs changes to Yjs
yield* takeEvery('store', function* (action) {
const patches = action.payload?.patches;
// meta is reliant on the patch to `schema.update()`
if (!patches || action.payload?.meta?.sync) return;

try {
for (const patch of patches) {
if (yjsAllowlist.includes(patch.path[0])) {
applyPatch(ydoc, patch);
}
}
} catch (error) {
console.error(error);
}
});
});

tsks.push(
thunks.bootup,
connectReduxDevToolsExtension({
Expand Down
11 changes: 11 additions & 0 deletions src/store/thunks/generic.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { updateStore, type AnyState, type StoreUpdater } from 'starfx';

import { thunks } from './foundation.ts';

export const updater = thunks.create<StoreUpdater<AnyState>[]>(
'update',
function* (ctx, next) {
yield* updateStore(ctx.payload);
yield* next();
}
);
2 changes: 2 additions & 0 deletions src/store/thunks/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
export { thunks } from './foundation.ts';
export * from './generic.ts';
export * from './sync.ts';
export * from './accounts.ts';
export * from './settings.ts';
export * from './transactions.ts';
Expand Down
10 changes: 10 additions & 0 deletions src/store/thunks/sync.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { type AnyState } from 'starfx';

import { schema } from '../schema.ts';
import { thunks } from './foundation.ts';

export const sync = thunks.create<AnyState>('sync', function* (ctx, next) {
// @ts-expect-error
yield* schema.update(ctx.payload, { sync: true });
yield* next();
});
Loading
Loading