@kestack/kabuki 0.0.0-stage → 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +274 -2
- package/dist/core/actor/actor.d.ts +42 -0
- package/dist/core/actor/actor.d.ts.map +1 -0
- package/dist/core/actor/actor.js +168 -0
- package/dist/core/actor/command.d.ts +24 -0
- package/dist/core/actor/command.d.ts.map +1 -0
- package/dist/core/actor/command.js +31 -0
- package/dist/core/actor/index.d.ts +6 -0
- package/dist/core/actor/index.d.ts.map +1 -0
- package/dist/core/actor/index.js +3 -0
- package/dist/core/actor/types.d.ts +295 -0
- package/dist/core/actor/types.d.ts.map +1 -0
- package/dist/core/actor/types.js +2 -0
- package/dist/core/history/history.d.ts +67 -0
- package/dist/core/history/history.d.ts.map +1 -0
- package/dist/core/history/history.js +145 -0
- package/dist/core/history/index.d.ts +5 -0
- package/dist/core/history/index.d.ts.map +1 -0
- package/dist/core/history/index.js +2 -0
- package/dist/core/history/replay.d.ts +34 -0
- package/dist/core/history/replay.d.ts.map +1 -0
- package/dist/core/history/replay.js +82 -0
- package/dist/core/history/saved.d.ts +7 -0
- package/dist/core/history/saved.d.ts.map +1 -0
- package/dist/core/history/saved.js +126 -0
- package/dist/core/index.d.ts +9 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +4 -0
- package/dist/core/runtime/index.d.ts +4 -0
- package/dist/core/runtime/index.d.ts.map +1 -0
- package/dist/core/runtime/index.js +1 -0
- package/dist/core/runtime/instance/command.d.ts +4 -0
- package/dist/core/runtime/instance/command.d.ts.map +1 -0
- package/dist/core/runtime/instance/command.js +12 -0
- package/dist/core/runtime/instance/index.d.ts +4 -0
- package/dist/core/runtime/instance/index.d.ts.map +1 -0
- package/dist/core/runtime/instance/index.js +1 -0
- package/dist/core/runtime/instance/instance.d.ts +16 -0
- package/dist/core/runtime/instance/instance.d.ts.map +1 -0
- package/dist/core/runtime/instance/instance.js +113 -0
- package/dist/core/runtime/instance/self.d.ts +35 -0
- package/dist/core/runtime/instance/self.d.ts.map +1 -0
- package/dist/core/runtime/instance/self.js +42 -0
- package/dist/core/runtime/instance/worker.d.ts +16 -0
- package/dist/core/runtime/instance/worker.d.ts.map +1 -0
- package/dist/core/runtime/instance/worker.js +52 -0
- package/dist/core/runtime/mailbox.d.ts +10 -0
- package/dist/core/runtime/mailbox.d.ts.map +1 -0
- package/dist/core/runtime/mailbox.js +64 -0
- package/dist/core/runtime/runtime.d.ts +36 -0
- package/dist/core/runtime/runtime.d.ts.map +1 -0
- package/dist/core/runtime/runtime.js +93 -0
- package/dist/core/runtime/time.d.ts +11 -0
- package/dist/core/runtime/time.d.ts.map +1 -0
- package/dist/core/runtime/time.js +22 -0
- package/dist/core/snapshot/handle.d.ts +14 -0
- package/dist/core/snapshot/handle.d.ts.map +1 -0
- package/dist/core/snapshot/handle.js +70 -0
- package/dist/core/snapshot/index.d.ts +8 -0
- package/dist/core/snapshot/index.d.ts.map +1 -0
- package/dist/core/snapshot/index.js +4 -0
- package/dist/core/snapshot/invoke.d.ts +17 -0
- package/dist/core/snapshot/invoke.d.ts.map +1 -0
- package/dist/core/snapshot/invoke.js +119 -0
- package/dist/core/snapshot/restore.d.ts +16 -0
- package/dist/core/snapshot/restore.d.ts.map +1 -0
- package/dist/core/snapshot/restore.js +63 -0
- package/dist/core/snapshot/snapshot.d.ts +61 -0
- package/dist/core/snapshot/snapshot.d.ts.map +1 -0
- package/dist/core/snapshot/snapshot.js +4 -0
- package/dist/core/snapshot/tree.d.ts +26 -0
- package/dist/core/snapshot/tree.d.ts.map +1 -0
- package/dist/core/snapshot/tree.js +77 -0
- package/dist/react/app.d.ts +49 -0
- package/dist/react/app.d.ts.map +1 -0
- package/dist/react/app.js +214 -0
- package/dist/react/child.d.ts +32 -0
- package/dist/react/child.d.ts.map +1 -0
- package/dist/react/child.js +80 -0
- package/dist/react/drawn.d.ts +4 -0
- package/dist/react/drawn.d.ts.map +1 -0
- package/dist/react/drawn.js +118 -0
- package/dist/react/index.d.ts +7 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +3 -0
- package/dist/react/registry.d.ts +12 -0
- package/dist/react/registry.d.ts.map +1 -0
- package/dist/react/registry.js +25 -0
- package/dist/react/view.d.ts +80 -0
- package/dist/react/view.d.ts.map +1 -0
- package/dist/react/view.js +47 -0
- package/package.json +55 -3
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
const KEY = Symbol.for('@kestack/kabuki/apps');
|
|
2
|
+
const apps = () => {
|
|
3
|
+
const global = globalThis;
|
|
4
|
+
return (global[KEY] ??= new Map());
|
|
5
|
+
};
|
|
6
|
+
export const registry = {
|
|
7
|
+
/** The History of the app mounted under this name, else the one its last mount left. */
|
|
8
|
+
history: (name) => apps().get(name)?.history,
|
|
9
|
+
/** Note that `app` is mounted with its History; false if another app of that name already is. */
|
|
10
|
+
claim: (name, app, history) => {
|
|
11
|
+
const live = apps().get(name)?.live;
|
|
12
|
+
if (live !== undefined && live !== app)
|
|
13
|
+
return false;
|
|
14
|
+
apps().set(name, { history, live: app });
|
|
15
|
+
return true;
|
|
16
|
+
},
|
|
17
|
+
/** Leave the History as the app closes. */
|
|
18
|
+
release: (name, app) => {
|
|
19
|
+
const kept = apps().get(name);
|
|
20
|
+
if (kept?.live === app)
|
|
21
|
+
apps().set(name, { history: kept.history });
|
|
22
|
+
},
|
|
23
|
+
/** Forget every app: for tests. */
|
|
24
|
+
clear: () => apps().clear(),
|
|
25
|
+
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { CSSProperties, ReactNode, Ref } from 'react';
|
|
2
|
+
import type { AnyActor, Many, Only, Types } from '../core/index.ts';
|
|
3
|
+
/** One Instance in the tree, to draw: its ID and, for a keyed Child, its key. Never changes. */
|
|
4
|
+
export interface Handle<A> {
|
|
5
|
+
readonly id: string;
|
|
6
|
+
readonly key?: string;
|
|
7
|
+
readonly actor?: A;
|
|
8
|
+
}
|
|
9
|
+
type HandleOf<S> = S extends Many<infer A> ? ReadonlyArray<Handle<A> & {
|
|
10
|
+
readonly key: string;
|
|
11
|
+
}> : Handle<S>;
|
|
12
|
+
type KidsIn<A, T> = T extends keyof Types<A>['children'] ? Types<A>['children'][T] : {};
|
|
13
|
+
/**
|
|
14
|
+
* What a parent may hand the root element of a View: the `ref` and `style`
|
|
15
|
+
* Framer Motion animates through. Typed so it spreads on any element.
|
|
16
|
+
*/
|
|
17
|
+
export interface Root {
|
|
18
|
+
readonly ref: Ref<never> | undefined;
|
|
19
|
+
readonly style: CSSProperties | undefined;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* What a View draws one Instance from, narrowed by its current State: `_tag`
|
|
23
|
+
* repeats the State's tag at the top so a `switch` on it narrows `state` and
|
|
24
|
+
* `children` together.
|
|
25
|
+
*/
|
|
26
|
+
export type ViewProps<A> = {
|
|
27
|
+
[T in Types<A>['state']['_tag']]: {
|
|
28
|
+
readonly _tag: T;
|
|
29
|
+
readonly id: string;
|
|
30
|
+
readonly model: Types<A>['model'];
|
|
31
|
+
readonly state: Only<Types<A>['state'], T>;
|
|
32
|
+
/** What its parent hands it now. */
|
|
33
|
+
readonly props: Types<A>['props'];
|
|
34
|
+
readonly children: {
|
|
35
|
+
readonly [K in keyof KidsIn<A, T>]: HandleOf<KidsIn<A, T>[K]>;
|
|
36
|
+
};
|
|
37
|
+
readonly send: (message: Types<A>['message']) => void;
|
|
38
|
+
/** Spread on the element the View draws as its root: `<li {...root}>`. */
|
|
39
|
+
readonly root: Root;
|
|
40
|
+
};
|
|
41
|
+
}[Types<A>['state']['_tag']];
|
|
42
|
+
/** The Instance's part of what a View draws from; `root` is added when drawing. */
|
|
43
|
+
export type Drawable = Omit<ViewProps<AnyActor>, 'root'>;
|
|
44
|
+
/** What Views read the app through, provided by `toReact`. */
|
|
45
|
+
export interface DrawnApp {
|
|
46
|
+
readonly subscribe: (listener: () => void) => () => void;
|
|
47
|
+
/** The Instance with this ID in the Snapshot drawn now, the same object while it does not change. */
|
|
48
|
+
readonly instance: (id: string) => Drawable | undefined;
|
|
49
|
+
/** The Instance as first drawn, before any Runtime: what a server draws and the client hydrates against. */
|
|
50
|
+
readonly served: (id: string) => Drawable | undefined;
|
|
51
|
+
}
|
|
52
|
+
export declare const AppContext: import("react").Context<DrawnApp | undefined>;
|
|
53
|
+
/**
|
|
54
|
+
* A View: a React component drawing one Instance of its Actor. A parent View hands it only where to draw: the Instance, and the
|
|
55
|
+
* `ref` and `style` to animate it by. Data from the parent is its Props, which
|
|
56
|
+
* the parent Actor hands it, so Update and Workers see it too.
|
|
57
|
+
*/
|
|
58
|
+
export type ViewOf<A> = (props: {
|
|
59
|
+
readonly instance: Handle<A>;
|
|
60
|
+
readonly ref?: Ref<Element> | undefined;
|
|
61
|
+
readonly style?: CSSProperties | undefined;
|
|
62
|
+
}) => ReactNode;
|
|
63
|
+
/**
|
|
64
|
+
* How one Actor is drawn: one function of its Instance. The result re-renders
|
|
65
|
+
* only when its own Instance's Model, State, Props or set of Children changes,
|
|
66
|
+
* or the parent hands it a new `ref` or `style`, never because a descendant
|
|
67
|
+
* did.
|
|
68
|
+
*
|
|
69
|
+
* Once the Instance stops, the View keeps drawing its last data, so an exit
|
|
70
|
+
* animation still has something to show.
|
|
71
|
+
*
|
|
72
|
+
* `style` counts as changed only when its values do: `motion.create` hands
|
|
73
|
+
* the View a new `style` object on every render, with the same values.
|
|
74
|
+
*/
|
|
75
|
+
declare const make: <A extends AnyActor>(actor: A, draw: (self: ViewProps<A>) => ReactNode) => ViewOf<A>;
|
|
76
|
+
export declare const View: {
|
|
77
|
+
make: typeof make;
|
|
78
|
+
};
|
|
79
|
+
export {};
|
|
80
|
+
//# sourceMappingURL=view.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"view.d.ts","sourceRoot":"","sources":["../../src/react/view.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { createContext, memo, use, useRef, useSyncExternalStore } from 'react';
|
|
2
|
+
export const AppContext = createContext(undefined);
|
|
3
|
+
/**
|
|
4
|
+
* How one Actor is drawn: one function of its Instance. The result re-renders
|
|
5
|
+
* only when its own Instance's Model, State, Props or set of Children changes,
|
|
6
|
+
* or the parent hands it a new `ref` or `style`, never because a descendant
|
|
7
|
+
* did.
|
|
8
|
+
*
|
|
9
|
+
* Once the Instance stops, the View keeps drawing its last data, so an exit
|
|
10
|
+
* animation still has something to show.
|
|
11
|
+
*
|
|
12
|
+
* `style` counts as changed only when its values do: `motion.create` hands
|
|
13
|
+
* the View a new `style` object on every render, with the same values.
|
|
14
|
+
*/
|
|
15
|
+
const make = (actor, draw) => {
|
|
16
|
+
const View = memo(({ instance, ref, style, }) => {
|
|
17
|
+
const drawn = use(AppContext);
|
|
18
|
+
if (!drawn)
|
|
19
|
+
throw new Error(`[@kestack/kabuki] ${actor.name}View is drawn outside its app`);
|
|
20
|
+
const live = useSyncExternalStore(drawn.subscribe, () => drawn.instance(instance.id), () => drawn.served(instance.id));
|
|
21
|
+
// The last data drawn, for a View still on screen after its Instance
|
|
22
|
+
// stopped. Written during render on purpose: it only ever holds what
|
|
23
|
+
// the store gave, so a render React throws away cannot leave it wrong.
|
|
24
|
+
const last = useRef(live);
|
|
25
|
+
if (live)
|
|
26
|
+
last.current = live;
|
|
27
|
+
const self = live ?? last.current;
|
|
28
|
+
if (!self)
|
|
29
|
+
return null;
|
|
30
|
+
return draw({
|
|
31
|
+
...self,
|
|
32
|
+
root: { ref: ref, style },
|
|
33
|
+
});
|
|
34
|
+
}, same);
|
|
35
|
+
View.displayName = `${actor.name}View`;
|
|
36
|
+
return View;
|
|
37
|
+
};
|
|
38
|
+
/** What the parent hands is the same when each is the same value, and `style` when each of its values is. */
|
|
39
|
+
const same = (a, b) => {
|
|
40
|
+
const keys = Object.keys(b);
|
|
41
|
+
if (keys.length !== Object.keys(a).length)
|
|
42
|
+
return false;
|
|
43
|
+
return keys.every((key) => Object.is(a[key], b[key]) ||
|
|
44
|
+
(key === 'style' && sameValues(a[key], b[key])));
|
|
45
|
+
};
|
|
46
|
+
const sameValues = (a, b) => a !== undefined && b !== undefined && same(a, b);
|
|
47
|
+
export const View = { make };
|
package/package.json
CHANGED
|
@@ -1,6 +1,58 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kestack/kabuki",
|
|
3
|
-
"version": "0.0.0
|
|
4
|
-
"
|
|
5
|
-
"description": "
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"description": "An app as one tree of Actors outside React: each Actor a small state machine driven by Messages, one immutable Snapshot for the whole app, Effect running the work, and React only drawing it.",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"effect",
|
|
8
|
+
"react",
|
|
9
|
+
"elm",
|
|
10
|
+
"state-machine",
|
|
11
|
+
"actor"
|
|
12
|
+
],
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "https://github.com/pkishorez/monorepo",
|
|
17
|
+
"directory": "packages/kabuki"
|
|
18
|
+
},
|
|
19
|
+
"type": "module",
|
|
20
|
+
"files": [
|
|
21
|
+
"README.md",
|
|
22
|
+
"LICENSE",
|
|
23
|
+
"dist/"
|
|
24
|
+
],
|
|
25
|
+
"exports": {
|
|
26
|
+
".": {
|
|
27
|
+
"types": "./dist/core/index.d.ts",
|
|
28
|
+
"default": "./dist/core/index.js"
|
|
29
|
+
},
|
|
30
|
+
"./react": {
|
|
31
|
+
"types": "./dist/react/index.d.ts",
|
|
32
|
+
"default": "./dist/react/index.js"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"peerDependencies": {
|
|
36
|
+
"effect": "^4.0.0",
|
|
37
|
+
"react": "^19.0.0"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@types/react": "19.2.18",
|
|
41
|
+
"@types/react-dom": "19.2.7",
|
|
42
|
+
"effect": "4.0.0",
|
|
43
|
+
"jsdom": "29.1.1",
|
|
44
|
+
"motion": "13.2.0",
|
|
45
|
+
"react": "19.2.8",
|
|
46
|
+
"react-dom": "19.2.8",
|
|
47
|
+
"typescript": "7.0.2",
|
|
48
|
+
"vite": "npm:@voidzero-dev/vite-plus-core@1.0.0",
|
|
49
|
+
"vitest": "5.0.1",
|
|
50
|
+
"laymos": "0.0.11"
|
|
51
|
+
},
|
|
52
|
+
"scripts": {
|
|
53
|
+
"build": "rm -rf ./dist && tsc -p tsconfig.build.json",
|
|
54
|
+
"fmt": "vp fmt",
|
|
55
|
+
"lint": "vp check && tsc --noEmit && laymos lint",
|
|
56
|
+
"test": "vitest run"
|
|
57
|
+
}
|
|
6
58
|
}
|