@slux/octane 0.0.0-stage → 0.1.1
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/CHANGELOG.md +45 -0
- package/LICENSE +21 -0
- package/README.md +275 -2
- package/dist/5.js +2 -0
- package/dist/5.js.map +1 -0
- package/dist/atom.d.ts +10 -0
- package/dist/atom.js +2 -0
- package/dist/atom.js.map +1 -0
- package/dist/flux.d.ts +7 -0
- package/dist/flux.js +2 -0
- package/dist/flux.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/proxy.d.ts +5 -0
- package/dist/proxy.js +2 -0
- package/dist/proxy.js.map +1 -0
- package/dist/rxjs.d.ts +5 -0
- package/dist/rxjs.js +2 -0
- package/dist/rxjs.js.map +1 -0
- package/dist/state-machine.d.ts +8 -0
- package/dist/state-machine.js +2 -0
- package/dist/state-machine.js.map +1 -0
- package/dist/store.d.ts +7 -0
- package/dist/store.js +2 -0
- package/dist/store.js.map +1 -0
- package/package.json +104 -3
- package/src/atom.ts +17 -0
- package/src/auto-import-api.ts +13 -0
- package/src/flux.ts +6 -0
- package/src/index.ts +31 -0
- package/src/proxy.ts +10 -0
- package/src/rxjs.ts +6 -0
- package/src/scope.ts +17 -0
- package/src/slots.ts +8 -0
- package/src/state-machine.ts +9 -0
- package/src/store.ts +39 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# @slux/octane
|
|
2
|
+
|
|
3
|
+
## 0.1.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 207e315: 基于 `v0.1.0` 之后的改动,完善包文档、交互示例和发布流程。
|
|
8
|
+
|
|
9
|
+
- 为 25 个包的 README 补充统一导航;文档站增加各包更新日志、版本发行说明和可在站内体验的示例入口。
|
|
10
|
+
- 整理示例应用的共享外壳与主题交互,并将示例图标替换为透明背景的 SVG。
|
|
11
|
+
- 修正自动导入 API 清单生成规则,排除需要显式调用的 `createAutoImportApis` 工厂函数,并统一 Viewfly 清单顺序。
|
|
12
|
+
- 发布工作流改为 GitHub OIDC 授信发布:预先打包各包、独立执行发布任务,并在发布成功后创建 GitHub Release 草稿。
|
|
13
|
+
|
|
14
|
+
- Updated dependencies [207e315]
|
|
15
|
+
- @slux/atom@0.1.1
|
|
16
|
+
- @slux/core@0.1.1
|
|
17
|
+
- @slux/flux@0.1.1
|
|
18
|
+
- @slux/proxy@0.1.1
|
|
19
|
+
- @slux/rxjs@0.1.1
|
|
20
|
+
- @slux/shared@0.1.1
|
|
21
|
+
- @slux/state-machine@0.1.1
|
|
22
|
+
- @slux/store@0.1.1
|
|
23
|
+
|
|
24
|
+
## 0.1.0
|
|
25
|
+
|
|
26
|
+
### Minor Changes
|
|
27
|
+
|
|
28
|
+
- 首次公开发布 slux 的 25 个包,统一版本为 0.1.0。
|
|
29
|
+
|
|
30
|
+
- 提供框架无关的响应式核心与共享工具,以及 Store、Atom、Proxy、Flux、状态机和 RxJS 六种状态组织方式。
|
|
31
|
+
- 提供持久化、历史记录、跨标签页同步、遥测和开发工具五类 Store 扩展。
|
|
32
|
+
- 提供 React、Vue、Solid、Svelte、Angular、Preact、Ripple、Inferno、Octane、Vitarx、ActView 和 Viewfly 绑定。
|
|
33
|
+
- 提供各包文档与对应示例,并接入发布检查和文档部署工作流。
|
|
34
|
+
|
|
35
|
+
### Patch Changes
|
|
36
|
+
|
|
37
|
+
- Updated dependencies
|
|
38
|
+
- @slux/atom@0.1.0
|
|
39
|
+
- @slux/core@0.1.0
|
|
40
|
+
- @slux/flux@0.1.0
|
|
41
|
+
- @slux/proxy@0.1.0
|
|
42
|
+
- @slux/rxjs@0.1.0
|
|
43
|
+
- @slux/shared@0.1.0
|
|
44
|
+
- @slux/state-machine@0.1.0
|
|
45
|
+
- @slux/store@0.1.0
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 itmanyong
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,276 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @slux/octane
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
为独立的 Octane UI 框架(npm 包 `octane`,peer 为 `^0.16.7`)提供基于**编译器 slot hooks** 的原生绑定。读取绑定通过 `useSyncExternalStore` **直接返回值 `T`**(不是 ref、Accessor 或 `.value`),写入仍走原实例。与其余框架适配包最根本的不同:本包 `exports` 全部指向 **`./src/*.ts` TypeScript 源码**,不是预编译产物,且**不提供 CommonJS 入口**。
|
|
4
|
+
|
|
5
|
+
> **前置条件(务必先读)**:消费端必须接入 Octane 官方编译器 / 构建插件(配合 `package.json` 的 `octane.hookSlots.manual: ["src"]`)来编译本包与调用方组件,编译器为每个 hook 调用点分配 slot。**不支持**把这些源文件直接交给普通 JSX/TS 编译器后运行。详见「前置条件与编译产物」。
|
|
6
|
+
|
|
7
|
+
## 特性与适用场景
|
|
8
|
+
|
|
9
|
+
| 场景 | 是否适用 | 说明 |
|
|
10
|
+
| ------------------------------------------------------------ | -------- | --------------------------- |
|
|
11
|
+
| 已接入 Octane 官方编译器的项目读取 core 状态 | 适用 | `useSignal` 直接返回值 `T` |
|
|
12
|
+
| 需要编译器 slot hooks 自动分配调用位置 | 适用 | 组件内无需手工传 `slot` |
|
|
13
|
+
| SSR 由工具链把 runtime 切到 `octane/server` | 适用 | `renderToString` 不建立订阅 |
|
|
14
|
+
| 接入 atom / store / proxy / flux / state-machine / rxjs 范式 | 适用 | 各子入口,返回值均为直接值 |
|
|
15
|
+
| 只想把 `.ts` 源码交给普通 JSX/TS 编译器直接运行 | 不适用 | 必须经 Octane 编译器 |
|
|
16
|
+
| 需要 CJS(`require`)入口 | 不适用 | 只有源码 ESM 入口 |
|
|
17
|
+
| 需要可写容器、`ref.set`、双向绑定 ref | 不适用 | 返回值是纯值,写入走原实例 |
|
|
18
|
+
| 需要自动请求级隔离、自动 SSR 序列化 / 恢复 | 不适用 | 沿用 core 显式覆盖语义 |
|
|
19
|
+
|
|
20
|
+
## 安装
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
pnpm add @slux/core @slux/octane octane
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
- `@slux/core`(`workspace:^`)与 `octane`(`^0.16.7`)是**必需 peer**;`@slux/shared` 是运行时依赖,会随包带入。
|
|
27
|
+
- **范式包是可选 peer,只装实际使用的**:`@slux/atom`、`@slux/store`、`@slux/proxy`、`@slux/flux`、`@slux/state-machine`、`@slux/rxjs`(均为 `workspace:^`,`peerDependenciesMeta` 标为 `optional`)。
|
|
28
|
+
- `exports` 全部是 `./src/*.ts` 源码路径,`main` / `module` / `types` 同为 `./src/index.ts`,`files` 包含 `src`;没有 `import` / `require` 条件区分,也没有 CJS 产物。`dist` 只有 ESM 构建产物供检查,**公开入口以 `src` 为准**。
|
|
29
|
+
- 消费端还需能编译本包的 Octane 编译器(见下节)。
|
|
30
|
+
|
|
31
|
+
`exports` 子入口逐个对应:
|
|
32
|
+
|
|
33
|
+
| 子入口 | 所需范式 peer | 主要导出 |
|
|
34
|
+
| ---------------------------- | --------------------- | --------------------------------------------------------------------------------------------------------------------- |
|
|
35
|
+
| `@slux/octane` | `@slux/core` | `useSignal`、`ScopeContext`、`ScopeProvider`、`useScopeContext`、`ScopeProviderProps`(类型)、`createAutoImportApis` |
|
|
36
|
+
| `@slux/octane/atom` | `@slux/atom` | `useAtom`、`useAtomValue`、`useSetAtom` |
|
|
37
|
+
| `@slux/octane/store` | `@slux/store` | `useStore` |
|
|
38
|
+
| `@slux/octane/proxy` | `@slux/proxy` | `useSnapshot` |
|
|
39
|
+
| `@slux/octane/flux` | `@slux/flux` | `useFluxStore` |
|
|
40
|
+
| `@slux/octane/state-machine` | `@slux/state-machine` | `useMachine` |
|
|
41
|
+
| `@slux/octane/rxjs` | `@slux/rxjs` | `useObservable` |
|
|
42
|
+
| `@slux/octane/package.json` | — | 包元信息 |
|
|
43
|
+
|
|
44
|
+
## 快速开始
|
|
45
|
+
|
|
46
|
+
组件文件由消费端 Octane 编译器处理,编译器自动为每个 hook 调用点分配 slot:
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
// Counter.tsx —— 由消费端 Octane 编译器编译
|
|
50
|
+
import { signal } from "@slux/core";
|
|
51
|
+
import { useSignal } from "@slux/octane";
|
|
52
|
+
import { atom } from "@slux/atom";
|
|
53
|
+
import { useAtom } from "@slux/octane/atom";
|
|
54
|
+
|
|
55
|
+
const count = signal(0);
|
|
56
|
+
const title = atom("hello");
|
|
57
|
+
|
|
58
|
+
export function Counter() {
|
|
59
|
+
const value = useSignal(count); // number:直接是值,不是 ref / accessor
|
|
60
|
+
const [text, setText] = useAtom(title); // [string, (value: string) => void]
|
|
61
|
+
return <output>{`${text}:${value}`}</output>;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// 写入仍走原实例 / 返回的 setter(可放在事件回调或外部 action 中):
|
|
65
|
+
count.set((n) => n + 1);
|
|
66
|
+
setText("world");
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
直接使用**未编译的原生 runtime** 编写时,需要给每个调用点传入独立、稳定的 `symbol` 作为最后一项参数:
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
const slot = Symbol("counter");
|
|
73
|
+
const value = useSignal(count, slot); // T
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## 前置条件与编译产物
|
|
77
|
+
|
|
78
|
+
- **源码即入口**:`exports` 直接指向 `./src/index.ts`、`./src/atom.ts` 等;`dist` 仅保留 ESM 构建产物供检查,不作为公开入口,且**不提供 CJS**。
|
|
79
|
+
- **hookSlots 声明**:`package.json` 的 `octane.hookSlots.manual: ["src"]` 让编译器把本包纳入 slot 编译。
|
|
80
|
+
- **slot 派生**:`src/slots.ts` 定义 `subSlot = createSubSlot({ tagPrefix: ":slux:", slotlessPrefix: "@slux/octane:", global: false })`。编译器给每个调用点分配 slot,内部子 hook 用 `subSlot(slot, "signal:subscribe")`、`"signal:read"`、`"signal:server"`、`"signal:value"` 等派生各自位置,避免相互覆盖。公开 API 的 `slot?: symbol` 是**最后一项可选参数**,编译后由编译器注入。
|
|
81
|
+
- **SSR 工具链**:服务端由工具链把 `octane` runtime 导入切换为 `octane/server`(`renderToString` 来自 `octane/server`),编译器按 client / server 模式处理组件;`octane/compiler` 的 `compile` 负责生成对应代码。
|
|
82
|
+
- **未编译 runtime**:若不经过编译器、直接在原生 runtime 上调用,必须为每个调用点传独立、稳定的 `symbol`,否则子 hook 位置会冲突。
|
|
83
|
+
|
|
84
|
+
## 主入口 API
|
|
85
|
+
|
|
86
|
+
| API | 签名 | 说明 |
|
|
87
|
+
| ---------------------- | -------------------------------------------------- | ------------------------------------------------------------------------------ |
|
|
88
|
+
| `useSignal` | `<T>(source: Subscribable<T>, slot?: symbol) => T` | 用 `useSyncExternalStore` 桥接,直接返回值 `T` |
|
|
89
|
+
| `ScopeProvider` | `({ scope, children }: ScopeProviderProps)` | 用 `createElement(ScopeContext, { value: scope, children })` 提供 core `Scope` |
|
|
90
|
+
| `ScopeContext` | `createContext<Scope \| null>(null)` 的结果 | 原生注入上下文,默认值 `null` |
|
|
91
|
+
| `useScopeContext` | `() => Scope \| null` | `useContext(ScopeContext)`,无提供时返回 `null` |
|
|
92
|
+
| `createAutoImportApis` | `(prefix?: string) => ImportsMap` | 生成自动导入配置,见「自动导入」 |
|
|
93
|
+
|
|
94
|
+
`useSignal` 内部由四个带 `subSlot` 的 hook 组成,行为如下:
|
|
95
|
+
|
|
96
|
+
| 维度 | 行为 |
|
|
97
|
+
| -------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
|
|
98
|
+
| 读取 | `getSnapshot = () => source.get()`,返回**客户端全局值** |
|
|
99
|
+
| **服务端快照** | `getServerSnapshot = () => (scope ? scope.get(source) : source.get())`,存在 `ScopeProvider` 时经 `scope.get` 读取 |
|
|
100
|
+
| 订阅 | `useCallback([source])` 内 `source.subscribe(notify)` + core `effect(() => source.get())` 驱动惰性 computed;返回同时释放两者的清理函数 |
|
|
101
|
+
| slot 派生 | `signal:subscribe` / `signal:read` / `signal:server` / `signal:value` 由 `subSlot` 派生 |
|
|
102
|
+
| 来源切换 | `useCallback` 依赖 `[source]`,切换 `source` 会清除旧订阅、重建读取 |
|
|
103
|
+
| 相等判定 | **不暴露 `equals` 参数**;快照变化由 `useSyncExternalStore` 依据 `getSnapshot()` 返回值判定 |
|
|
104
|
+
|
|
105
|
+
## 子入口 API
|
|
106
|
+
|
|
107
|
+
所有读取绑定都**直接返回值**,写入一律通过原实例;每个 API 都带 `slot?: symbol` 末位参数。`/atom`、`/store`、`/proxy` 的 `getServerSnapshot` 等于 `getSnapshot`(不读 Scope)。
|
|
108
|
+
|
|
109
|
+
### `@slux/octane/atom`
|
|
110
|
+
|
|
111
|
+
| API | 签名 | 返回值 | 写入方式 |
|
|
112
|
+
| -------------- | -------------------------------------------------------------- | -------------- | ------------------------------------------- |
|
|
113
|
+
| `useAtom` | `<T>(atom: Atom<T>, slot?: symbol) => [T, (value: T) => void]` | `[值, setter]` | setter 直接调用 `atom.set(value)` |
|
|
114
|
+
| `useAtomValue` | `<T>(atom: AnyReadableAtom<T>, slot?: symbol) => T` | 值 `T` | 只读;可写派生 atom 通过原 `atom.write` |
|
|
115
|
+
| `useSetAtom` | `<T>(atom: Atom<T>, slot?: symbol) => (value: T) => void` | setter | `atom.set(value)`,函数值不会被当成 updater |
|
|
116
|
+
|
|
117
|
+
三者用 `useSyncExternalStore(subscribe, getSnapshot, getSnapshot, …)`,子 hook 位置由 `subSlot(slot, "atom:…")` 派生。
|
|
118
|
+
|
|
119
|
+
### `@slux/octane/store`
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
useStore<State extends object>(store: StoreApi<State>, slot?: symbol): State;
|
|
123
|
+
useStore<State extends object, Selection>(
|
|
124
|
+
store: StoreApi<State>,
|
|
125
|
+
selector: (state: State) => Selection,
|
|
126
|
+
equalityFn?: (previous: Selection, next: Selection) => boolean,
|
|
127
|
+
slot?: symbol,
|
|
128
|
+
): Selection;
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
- `slot` 是「最后一项参数」,可落在第 2 / 3 / 4 个位置,实现用 `typeof arg === "symbol"` 辨识出真正的 slot、`selector` 与 `equalityFn`。
|
|
132
|
+
- `getSnapshot` 由 `useMemo` 构建并缓存 `{ state, selection }`:`Object.is(cache.state, state)` 时直接返回缓存;否则 `equalityFn(cache.selection, next)` 为 `true` 时保留旧引用。`equalityFn` 默认 `Object.is`。
|
|
133
|
+
- 写入走 `store.setState`;`getServerSnapshot = getSnapshot`,不读 Scope。
|
|
134
|
+
|
|
135
|
+
### `@slux/octane/proxy`
|
|
136
|
+
|
|
137
|
+
`useSnapshot<T extends object>(proxy: T, slot?: symbol): Snapshot<T>`:返回**冻结且结构共享**的快照,订阅整个 proxy 及嵌套变化;通过原 proxy 对象修改状态。
|
|
138
|
+
|
|
139
|
+
### `@slux/octane/flux`
|
|
140
|
+
|
|
141
|
+
`useFluxStore<State, Action extends { type: string }>(store: FluxStore<State, Action>, slot?: symbol): State`,等价于 `useSignal(store, slot)`。写入仍通过 `store.dispatch(action)`。
|
|
142
|
+
|
|
143
|
+
### `@slux/octane/state-machine`
|
|
144
|
+
|
|
145
|
+
`useMachine<Context, Event extends { type: string }, State extends string>(machine: StateMachine<Context, Event, State>, slot?: symbol): MachineSnapshot<Context, State>`,等价于 `useSignal(machine, slot)`。写入通过 `machine.send(event)`;返回的是快照对象,状态名与上下文分别读作 `snapshot.value`、`snapshot.context`。
|
|
146
|
+
|
|
147
|
+
### `@slux/octane/rxjs`
|
|
148
|
+
|
|
149
|
+
`useObservable<T>(source: ObservableState<T>, slot?: symbol): T`,等价于 `useSignal(source, slot)`。写入通过上游 Observable / Subject;`status` / `error` 从来源读取,`dispose()` 由桥接创建者负责。
|
|
150
|
+
|
|
151
|
+
> `/flux`、`/state-machine`、`/rxjs` 复用 `useSignal`,因此它们的服务端快照同样经过 `scope.get(source)`;但 core 的 `scope.set` 要求 `Signal<T>`,范式实例不提供 `set`,所以对这些实例无法建立 Scope 覆盖,服务端读到的仍是 `scope.get` 回退后的来源当前值。
|
|
152
|
+
|
|
153
|
+
## 专属语义与边界
|
|
154
|
+
|
|
155
|
+
- **SSR / hydration**:`renderToString` 来自 `octane/server`(由工具链切换 runtime 导入),**不建立任何组件订阅**(`subscribe` 不被调用),也不改写全局 signal。只有主入口 `useSignal`(及复用它的三个新范式)的服务端快照在存在 `ScopeProvider` 时走 `scope.get(source)`;`/atom`、`/store`、`/proxy` 的 `getServerSnapshot` 直接等于 `getSnapshot`,不隐式使用 core Scope。服务端不自动序列化、传输或恢复状态,SSR 与上下文的通用约定见仓库根 [README](../../README.md) 的「框架适配 · 通用约定」。
|
|
156
|
+
- **Scope**:用 `<ScopeProvider scope={scope}>` 提供,后代 `useScopeContext()` 返回 `Scope | null`;`ScopeContext` 可用于原生注入配置。客户端 `getSnapshot` 始终读 `source.get()`,Scope **不改变客户端全局值**。Scope 沿用 core 的**显式覆盖**语义,`runInScope` 不改写全局 signal 读写。
|
|
157
|
+
- **订阅与清理**:订阅与惰性 computed 的 core `effect` 由 `useSyncExternalStore` 管理;`useCallback` / `useMemo` 的依赖(`[source]`、`[store, selector, equalityFn]`、`[atom]`、`[proxy]`)保证切换来源时清除旧订阅、重建读取。`root.unmount()` 释放所有订阅与 effect;卸载只解除**组件自身**绑定,flux / machine / observable 原实例不随之销毁,`dispose()` 由调用方负责。
|
|
158
|
+
- **快照相等判定**:`useSignal` 无 `equals` 参数;`useStore` 的 `equalityFn` 默认 `Object.is`,相等时保留旧选择结果引用,并用 `useMemo` 缓存保证 `getSnapshot` 返回稳定值;proxy 快照冻结、未修改分支保持原引用。
|
|
159
|
+
- **错误处理**:绑定层不额外捕获异常、不吞错、不回滚写入;`source.get` / `subscribe` / core `effect` 抛出的错误按 `@slux/core` 的语义传播。
|
|
160
|
+
|
|
161
|
+
## 自动导入
|
|
162
|
+
|
|
163
|
+
`createAutoImportApis(prefix?)` 基于 `@slux/shared` 的 `generateBatchAutoImports` 返回 `unplugin-auto-import` 的 `ImportsMap`,来源名为 `@slux/octane`。登记清单(取自 `src/auto-import-api.ts` 的 `allImportApis`):
|
|
164
|
+
|
|
165
|
+
`ScopeContext`、`ScopeProvider`、`useScopeContext`、`useSignal`
|
|
166
|
+
|
|
167
|
+
```ts
|
|
168
|
+
import AutoImport from "unplugin-auto-import/vite";
|
|
169
|
+
import { createAutoImportApis } from "@slux/octane";
|
|
170
|
+
|
|
171
|
+
export default {
|
|
172
|
+
plugins: [AutoImport({ imports: [createAutoImportApis()] })],
|
|
173
|
+
};
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
传入前缀会为登记项生成别名;前缀以小写字母结尾时 API 名首字母大写,规则由 `@slux/shared` 的 `createAutoImports` 统一决定。`ScopeProviderProps` 是纯类型,不在登记清单中。
|
|
177
|
+
|
|
178
|
+
## 类型导出
|
|
179
|
+
|
|
180
|
+
主入口 `src/index.ts` 唯一 re-export 的类型是 `ScopeProviderProps`(`{ readonly scope: Scope; readonly children?: OctaneNode }`);各子入口不 re-export 类型。其余类型从来源包导入:
|
|
181
|
+
|
|
182
|
+
| 用到的类型 | 来源 |
|
|
183
|
+
| --------------------------------- | ------------------------ |
|
|
184
|
+
| `ScopeProviderProps` | `@slux/octane`(主入口) |
|
|
185
|
+
| `Subscribable<T>`、`Scope` | `@slux/core` |
|
|
186
|
+
| `OctaneNode` | `octane` |
|
|
187
|
+
| `Atom<T>`、`AnyReadableAtom<T>` | `@slux/atom` |
|
|
188
|
+
| `StoreApi<T>` | `@slux/store` |
|
|
189
|
+
| `Snapshot<T>` | `@slux/proxy` |
|
|
190
|
+
| `FluxStore` | `@slux/flux` |
|
|
191
|
+
| `MachineSnapshot`、`StateMachine` | `@slux/state-machine` |
|
|
192
|
+
| `ObservableState<T>` | `@slux/rxjs` |
|
|
193
|
+
|
|
194
|
+
## 不适用的场景
|
|
195
|
+
|
|
196
|
+
- 未接入 Octane 官方编译器、只想把 `.ts` 当普通 TSX 编译后运行:不支持,slot 无法分配。
|
|
197
|
+
- 需要 CJS / `require` 入口:本包不提供。
|
|
198
|
+
- 需要可写容器或在绑定上直接 `set`:返回值是纯值,请用原实例写入。
|
|
199
|
+
- 用未编译的原生 runtime 却不愿为每处调用传稳定 `symbol`:子 hook 位置会冲突。
|
|
200
|
+
- 需要框架自动做请求级状态隔离或 SSR 状态恢复:需按 core 的 `Scope` 与服务端约定自行实现。
|
|
201
|
+
|
|
202
|
+
## 版本、许可与反馈
|
|
203
|
+
|
|
204
|
+
- 版本:`0.1.0`(`package.json`)。
|
|
205
|
+
- 许可:MIT,见仓库根 [LICENSE](../../LICENSE)。
|
|
206
|
+
- 问题反馈:通过 [issues](https://github.com/itmanyong/slux/issues) 提交(`package.json` `repository`)。
|
|
207
|
+
- 通用约定(子入口分布、写入方式、桥接生命周期、Scope、SSR)与本地开发命令见仓库根 [README](../../README.md) 的「框架适配 · 通用约定」与「本地开发」,本文不重复。
|
|
208
|
+
|
|
209
|
+
<!-- package-navigation:start -->
|
|
210
|
+
|
|
211
|
+
## 包导航
|
|
212
|
+
|
|
213
|
+
### 核心层
|
|
214
|
+
|
|
215
|
+
响应式运行时与零依赖工具,其余各层都建立在 core 之上。
|
|
216
|
+
|
|
217
|
+
| 包名 | 版本 | 在线文档 | 功能 |
|
|
218
|
+
| ----------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ | ---------------------- |
|
|
219
|
+
| [`@slux/core`](https://github.com/itmanyong/slux/tree/main/packages/core) | [](https://www.npmjs.com/package/@slux/core) [](https://npmx.dev/package/@slux/core) | [在线文档](https://itmanyong.github.io/slux/packages/core) | 框架无关的响应式运行时 |
|
|
220
|
+
| [`@slux/shared`](https://github.com/itmanyong/slux/tree/main/packages/shared) | [](https://www.npmjs.com/package/@slux/shared) [](https://npmx.dev/package/@slux/shared) | [在线文档](https://itmanyong.github.io/slux/packages/shared) | 零依赖的共享工具 |
|
|
221
|
+
|
|
222
|
+
### 范式层
|
|
223
|
+
|
|
224
|
+
六种组织状态的方式,共享 core 的运行时,可在同一个依赖图中混用。
|
|
225
|
+
|
|
226
|
+
| 包名 | 版本 | 在线文档 | 功能 |
|
|
227
|
+
| ------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- | ---------------------------------- |
|
|
228
|
+
| [`@slux/store`](https://github.com/itmanyong/slux/tree/main/packages/store) | [](https://www.npmjs.com/package/@slux/store) [](https://npmx.dev/package/@slux/store) | [在线文档](https://itmanyong.github.io/slux/packages/store) | 状态与 action 集中管理,支持中间件 |
|
|
229
|
+
| [`@slux/atom`](https://github.com/itmanyong/slux/tree/main/packages/atom) | [](https://www.npmjs.com/package/@slux/atom) [](https://npmx.dev/package/@slux/atom) | [在线文档](https://itmanyong.github.io/slux/packages/atom) | 细粒度原子状态与派生 |
|
|
230
|
+
| [`@slux/proxy`](https://github.com/itmanyong/slux/tree/main/packages/proxy) | [](https://www.npmjs.com/package/@slux/proxy) [](https://npmx.dev/package/@slux/proxy) | [在线文档](https://itmanyong.github.io/slux/packages/proxy) | 可变代理写入与只读快照 |
|
|
231
|
+
| [`@slux/flux`](https://github.com/itmanyong/slux/tree/main/packages/flux) | [](https://www.npmjs.com/package/@slux/flux) [](https://npmx.dev/package/@slux/flux) | [在线文档](https://itmanyong.github.io/slux/packages/flux) | reducer + dispatch 范式 |
|
|
232
|
+
| [`@slux/state-machine`](https://github.com/itmanyong/slux/tree/main/packages/state-machine) | [](https://www.npmjs.com/package/@slux/state-machine) [](https://npmx.dev/package/@slux/state-machine) | [在线文档](https://itmanyong.github.io/slux/packages/state-machine) | 平面有限状态机 |
|
|
233
|
+
| [`@slux/rxjs`](https://github.com/itmanyong/slux/tree/main/packages/rxjs) | [](https://www.npmjs.com/package/@slux/rxjs) [](https://npmx.dev/package/@slux/rxjs) | [在线文档](https://itmanyong.github.io/slux/packages/rxjs) | Observable 与状态双向桥接 |
|
|
234
|
+
|
|
235
|
+
### 中间件层
|
|
236
|
+
|
|
237
|
+
挂载到 Store 的横切能力,五个包共用同一套契约。
|
|
238
|
+
|
|
239
|
+
| 包名 | 版本 | 在线文档 | 功能 |
|
|
240
|
+
| ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- | -------------------- |
|
|
241
|
+
| [`@slux/storage`](https://github.com/itmanyong/slux/tree/main/packages/storage) | [](https://www.npmjs.com/package/@slux/storage) [](https://npmx.dev/package/@slux/storage) | [在线文档](https://itmanyong.github.io/slux/packages/storage) | 状态持久化与恢复 |
|
|
242
|
+
| [`@slux/history`](https://github.com/itmanyong/slux/tree/main/packages/history) | [](https://www.npmjs.com/package/@slux/history) [](https://npmx.dev/package/@slux/history) | [在线文档](https://itmanyong.github.io/slux/packages/history) | 撤销与重做 |
|
|
243
|
+
| [`@slux/tab-sync`](https://github.com/itmanyong/slux/tree/main/packages/tab-sync) | [](https://www.npmjs.com/package/@slux/tab-sync) [](https://npmx.dev/package/@slux/tab-sync) | [在线文档](https://itmanyong.github.io/slux/packages/tab-sync) | 跨标签页状态同步 |
|
|
244
|
+
| [`@slux/telemetry`](https://github.com/itmanyong/slux/tree/main/packages/telemetry) | [](https://www.npmjs.com/package/@slux/telemetry) [](https://npmx.dev/package/@slux/telemetry) | [在线文档](https://itmanyong.github.io/slux/packages/telemetry) | 状态变化与订阅遥测 |
|
|
245
|
+
| [`@slux/devtools`](https://github.com/itmanyong/slux/tree/main/packages/devtools) | [](https://www.npmjs.com/package/@slux/devtools) [](https://npmx.dev/package/@slux/devtools) | [在线文档](https://itmanyong.github.io/slux/packages/devtools) | 调试工具、日志与检查 |
|
|
246
|
+
|
|
247
|
+
### 框架层
|
|
248
|
+
|
|
249
|
+
十二个绑定包都提供 core 主入口与六个范式子入口,使用框架原生响应式与生命周期。范式包是**可选** peer,只装实际使用的;`@slux/core` 与框架本身是**必需** peer。
|
|
250
|
+
|
|
251
|
+
| 包名 | 版本 | 在线文档 | 适配框架 | 适配版本 | 功能 |
|
|
252
|
+
| ------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | ----------------------------------------------- | ------------------------------------------ | ---------------------- |
|
|
253
|
+
| [`@slux/react`](https://github.com/itmanyong/slux/tree/main/packages/react) | [](https://www.npmjs.com/package/@slux/react) [](https://npmx.dev/package/@slux/react) | [在线文档](https://itmanyong.github.io/slux/packages/react) | [React](https://github.com/react/react) | `^18.0.0 ‖ ^19.0.0` | React 组件状态绑定 |
|
|
254
|
+
| [`@slux/vue`](https://github.com/itmanyong/slux/tree/main/packages/vue) | [](https://www.npmjs.com/package/@slux/vue) [](https://npmx.dev/package/@slux/vue) | [在线文档](https://itmanyong.github.io/slux/packages/vue) | [Vue](https://github.com/vuejs/core) | `>= 3.2.0` | Vue 组合式 API 绑定 |
|
|
255
|
+
| [`@slux/solid`](https://github.com/itmanyong/slux/tree/main/packages/solid) | [](https://www.npmjs.com/package/@slux/solid) [](https://npmx.dev/package/@slux/solid) | [在线文档](https://itmanyong.github.io/slux/packages/solid) | [Solid](https://github.com/solidjs/solid) | `>= 1.0.0` | Solid 细粒度响应式绑定 |
|
|
256
|
+
| [`@slux/svelte`](https://github.com/itmanyong/slux/tree/main/packages/svelte) | [](https://www.npmjs.com/package/@slux/svelte) [](https://npmx.dev/package/@slux/svelte) | [在线文档](https://itmanyong.github.io/slux/packages/svelte) | [Svelte](https://github.com/sveltejs/svelte) | `>= 4.0.0` | Svelte 原生 store 绑定 |
|
|
257
|
+
| [`@slux/angular`](https://github.com/itmanyong/slux/tree/main/packages/angular) | [](https://www.npmjs.com/package/@slux/angular) [](https://npmx.dev/package/@slux/angular) | [在线文档](https://itmanyong.github.io/slux/packages/angular) | [Angular](https://github.com/angular/angular) | `@angular/core >= 16.0.0`、`rxjs >= 7.8.0` | Angular 注入上下文绑定 |
|
|
258
|
+
| [`@slux/preact`](https://github.com/itmanyong/slux/tree/main/packages/preact) | [](https://www.npmjs.com/package/@slux/preact) [](https://npmx.dev/package/@slux/preact) | [在线文档](https://itmanyong.github.io/slux/packages/preact) | [Preact](https://github.com/preactjs/preact) | `>= 10.5.0` | Preact 组件状态绑定 |
|
|
259
|
+
| [`@slux/ripple`](https://github.com/itmanyong/slux/tree/main/packages/ripple) | [](https://www.npmjs.com/package/@slux/ripple) [](https://npmx.dev/package/@slux/ripple) | [在线文档](https://itmanyong.github.io/slux/packages/ripple) | [Ripple](https://github.com/Ripple-TS/ripple) | `>= 0.6.0` | Ripple 组件状态绑定 |
|
|
260
|
+
| [`@slux/inferno`](https://github.com/itmanyong/slux/tree/main/packages/inferno) | [](https://www.npmjs.com/package/@slux/inferno) [](https://npmx.dev/package/@slux/inferno) | [在线文档](https://itmanyong.github.io/slux/packages/inferno) | [Inferno](https://github.com/infernojs/inferno) | `>= 7.0.0` | Inferno 组件状态绑定 |
|
|
261
|
+
| [`@slux/octane`](https://github.com/itmanyong/slux/tree/main/packages/octane) | [](https://www.npmjs.com/package/@slux/octane) [](https://npmx.dev/package/@slux/octane) | [在线文档](https://itmanyong.github.io/slux/packages/octane) | [Octane](https://github.com/octanejs/octane) | `>= 0.16.0` | Octane 编译器绑定 |
|
|
262
|
+
| [`@slux/vitarx`](https://github.com/itmanyong/slux/tree/main/packages/vitarx) | [](https://www.npmjs.com/package/@slux/vitarx) [](https://npmx.dev/package/@slux/vitarx) | [在线文档](https://itmanyong.github.io/slux/packages/vitarx) | [Vitarx](https://github.com/vitarx-lib/core) | `>= 4.0.0` | Vitarx 组件状态绑定 |
|
|
263
|
+
| [`@slux/actview`](https://github.com/itmanyong/slux/tree/main/packages/actview) | [](https://www.npmjs.com/package/@slux/actview) [](https://npmx.dev/package/@slux/actview) | [在线文档](https://itmanyong.github.io/slux/packages/actview) | [ActView](https://github.com/actview/actview) | `>= 1.0.0` | ActView 组件状态绑定 |
|
|
264
|
+
| [`@slux/viewfly`](https://github.com/itmanyong/slux/tree/main/packages/viewfly) | [](https://www.npmjs.com/package/@slux/viewfly) [](https://npmx.dev/package/@slux/viewfly) | [在线文档](https://itmanyong.github.io/slux/packages/viewfly) | [Viewfly](https://github.com/viewfly/viewfly) | `>= 3.0.0` | Viewfly 组件状态绑定 |
|
|
265
|
+
|
|
266
|
+
格式差异:**Octane** 的公开入口是 TypeScript 源码,需消费端 Octane 编译器处理且不提供 CJS;**Ripple** 只发布 ESM,另有 `./server` 服务端入口。其余边界见各包文档。
|
|
267
|
+
|
|
268
|
+
### 插件层
|
|
269
|
+
|
|
270
|
+
**规划中**
|
|
271
|
+
|
|
272
|
+
| 包名 | 版本 | 在线文档 | 功能 |
|
|
273
|
+
| ---- | ---- | -------- | ---- |
|
|
274
|
+
| — | — | — | — |
|
|
275
|
+
|
|
276
|
+
<!-- package-navigation:end -->
|
package/dist/5.js
ADDED
package/dist/5.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"5.js","sources":["../src/slots.ts"],"sourcesContent":["import { createSubSlot } from \"octane\";\n\n// 编译器给每个调用点分配 slot;子 hook 必须派生自己的位置,避免相互覆盖。\nexport const subSlot = createSubSlot({\n tagPrefix: \":slux:\",\n slotlessPrefix: \"@slux/octane:\",\n global: false,\n});\n"],"names":["subSlot","createSubSlot"],"mappings":"uCAGO,IAAMA,EAAUC,EAAc,CACnC,UAAW,SACX,eAAgB,gBAChB,OAAQ,EACV,U"}
|
package/dist/atom.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { AnyReadableAtom } from '@slux/atom';
|
|
2
|
+
import type { Atom } from '@slux/atom';
|
|
3
|
+
|
|
4
|
+
export declare function useAtom<T>(atom: Atom<T>, slot?: symbol): [T, (value: T) => void];
|
|
5
|
+
|
|
6
|
+
export declare function useAtomValue<T>(atom: AnyReadableAtom<T>, slot?: symbol): T;
|
|
7
|
+
|
|
8
|
+
export declare function useSetAtom<T>(atom: Atom<T>, slot?: symbol): (value: T) => void;
|
|
9
|
+
|
|
10
|
+
export { }
|
package/dist/atom.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{useCallback as t,useSyncExternalStore as e}from"octane";import{subSlot as o}from"./5.js";function r(t,e){return[m(t,o(e,"atom:value")),u(t,o(e,"atom:setter"))]}function m(r,m){let u=t(t=>r.subscribe(t),[r],o(m,"atom:subscribe")),a=t(()=>r.get(),[r],o(m,"atom:read"));return e(u,a,a,o(m,"atom:store"))}function u(e,r){return t(t=>e.set(t),[e],o(r,"atom:write"))}export{r as useAtom,m as useAtomValue,u as useSetAtom};
|
|
2
|
+
//# sourceMappingURL=atom.js.map
|
package/dist/atom.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"atom.js","sources":["../src/atom.ts"],"sourcesContent":["import { useCallback, useSyncExternalStore } from \"octane\";\nimport type { Atom, AnyReadableAtom } from \"@slux/atom\";\nimport { subSlot } from \"./slots.js\";\n\nexport function useAtom<T>(atom: Atom<T>, slot?: symbol): [T, (value: T) => void] {\n return [useAtomValue(atom, subSlot(slot, \"atom:value\")), useSetAtom(atom, subSlot(slot, \"atom:setter\"))];\n}\n\nexport function useAtomValue<T>(atom: AnyReadableAtom<T>, slot?: symbol): T {\n const subscribe = useCallback((notify: () => void) => atom.subscribe(notify), [atom], subSlot(slot, \"atom:subscribe\"));\n const getSnapshot = useCallback(() => atom.get(), [atom], subSlot(slot, \"atom:read\"));\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot, subSlot(slot, \"atom:store\"));\n}\n\nexport function useSetAtom<T>(atom: Atom<T>, slot?: symbol): (value: T) => void {\n return useCallback((value: T) => atom.set(value), [atom], subSlot(slot, \"atom:write\"));\n}\n"],"names":["useAtom","atom","slot","useAtomValue","subSlot","useSetAtom","subscribe","useCallback","notify","getSnapshot","useSyncExternalStore","value"],"mappings":"8GAIO,SAASA,EAAWC,CAAa,CAAEC,CAAa,EACrD,MAAO,CAACC,EAAaF,EAAMG,EAAQF,EAAM,eAAgBG,EAAWJ,EAAMG,EAAQF,EAAM,gBAAgB,AAC1G,CAEO,SAASC,EAAgBF,CAAwB,CAAEC,CAAa,EACrE,IAAMI,EAAYC,EAAY,AAACC,GAAuBP,EAAK,SAAS,CAACO,GAAS,CAACP,EAAK,CAAEG,EAAQF,EAAM,mBAC9FO,EAAcF,EAAY,IAAMN,EAAK,GAAG,GAAI,CAACA,EAAK,CAAEG,EAAQF,EAAM,cACxE,OAAOQ,EAAqBJ,EAAWG,EAAaA,EAAaL,EAAQF,EAAM,cACjF,CAEO,SAASG,EAAcJ,CAAa,CAAEC,CAAa,EACxD,OAAOK,EAAY,AAACI,GAAaV,EAAK,GAAG,CAACU,GAAQ,CAACV,EAAK,CAAEG,EAAQF,EAAM,cAC1E,Q"}
|
package/dist/flux.d.ts
ADDED
package/dist/flux.js
ADDED
package/dist/flux.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flux.js","sources":["../src/flux.ts"],"sourcesContent":["import type { FluxStore } from \"@slux/flux\";\nimport { useSignal } from \"./index.js\";\n\nexport function useFluxStore<State, Action extends { type: string }>(store: FluxStore<State, Action>, slot?: symbol): State {\n return useSignal(store, slot);\n}\n"],"names":["useFluxStore","store","slot","useSignal"],"mappings":"iDAGO,SAASA,EAAqDC,CAA+B,CAAEC,CAAa,EACjH,OAAOC,EAAUF,EAAOC,EAC1B,Q"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Context } from 'octane';
|
|
2
|
+
import { ElementDescriptor } from 'octane';
|
|
3
|
+
import type { OctaneNode } from 'octane';
|
|
4
|
+
import type { Scope } from '@slux/core';
|
|
5
|
+
import type { Subscribable } from '@slux/core';
|
|
6
|
+
|
|
7
|
+
export declare const createAutoImportApis: (prefix?: string) => {
|
|
8
|
+
[x: string]: (string | [string, string])[];
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export declare const ScopeContext: Context<Scope | null>;
|
|
12
|
+
|
|
13
|
+
export declare function ScopeProvider({ scope, children }: ScopeProviderProps): ElementDescriptor< {
|
|
14
|
+
value: Scope;
|
|
15
|
+
children: unknown;
|
|
16
|
+
}>;
|
|
17
|
+
|
|
18
|
+
export declare type ScopeProviderProps = {
|
|
19
|
+
readonly scope: Scope;
|
|
20
|
+
readonly children?: OctaneNode;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export declare function useScopeContext(): Scope | null;
|
|
24
|
+
|
|
25
|
+
export declare function useSignal<T>(source: Subscribable<T>, slot?: symbol): T;
|
|
26
|
+
|
|
27
|
+
export { }
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{createContext as e,createElement as t,useCallback as r,useContext as o,useSyncExternalStore as n}from"octane";import{effect as u}from"@slux/core";import{generateBatchAutoImports as i}from"@slux/shared";import{subSlot as s}from"./5.js";let l=e(null);function c({scope:e,children:r}){return t(l,{value:e,children:r})}function a(){return o(l)}let p=["ScopeContext","ScopeProvider","useScopeContext","useSignal"],m=(e="")=>i([{prefix:e,fromName:"@slux/octane",imports:p}]);function g(e,t){let o=a();return n(r(t=>{let r=e.subscribe(t),o=u(()=>{e.get()});return()=>{r(),o()}},[e],s(t,"signal:subscribe")),r(()=>e.get(),[e],s(t,"signal:read")),r(()=>o?o.get(e):e.get(),[o,e],s(t,"signal:server")),s(t,"signal:value"))}export{l as ScopeContext,c as ScopeProvider,m as createAutoImportApis,a as useScopeContext,g as useSignal};
|
|
2
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/scope.ts","../src/auto-import-api.ts","../src/index.ts"],"sourcesContent":["import { createContext, createElement, useContext } from \"octane\";\nimport type { OctaneNode } from \"octane\";\nimport type { Scope } from \"@slux/core\";\n\nexport type ScopeProviderProps = {\n readonly scope: Scope;\n readonly children?: OctaneNode;\n};\nexport const ScopeContext = createContext<Scope | null>(null);\n\nexport function ScopeProvider({ scope, children }: ScopeProviderProps) {\n return createElement(ScopeContext, { value: scope, children });\n}\n\nexport function useScopeContext(): Scope | null {\n return useContext(ScopeContext);\n}\n","import { generateBatchAutoImports } from \"@slux/shared\";\n\nconst allImportApis: string[] = [\"ScopeContext\", \"ScopeProvider\", \"useScopeContext\", \"useSignal\"];\n\nexport const createAutoImportApis = (prefix: string = \"\") => {\n return generateBatchAutoImports([\n {\n prefix,\n fromName: \"@slux/octane\",\n imports: allImportApis,\n },\n ]);\n};\n","import { useCallback, useSyncExternalStore } from \"octane\";\nimport { effect } from \"@slux/core\";\nimport type { Subscribable } from \"@slux/core\";\nimport { useScopeContext } from \"./scope.js\";\nimport { subSlot } from \"./slots.js\";\n\nexport { ScopeContext, ScopeProvider, useScopeContext } from \"./scope.js\";\nexport type { ScopeProviderProps } from \"./scope.js\";\n\nexport function useSignal<T>(source: Subscribable<T>, slot?: symbol): T {\n const scope = useScopeContext();\n const subscribe = useCallback(\n (notify: () => void) => {\n const unsubscribe = source.subscribe(notify);\n // core computed 按读取刷新,保持 effect 才能持续接收依赖变化。\n const dispose = effect(() => {\n source.get();\n });\n return () => {\n unsubscribe();\n dispose();\n };\n },\n [source],\n subSlot(slot, \"signal:subscribe\"),\n );\n const getSnapshot = useCallback(() => source.get(), [source], subSlot(slot, \"signal:read\"));\n const getServerSnapshot = useCallback(() => (scope ? scope.get(source) : source.get()), [scope, source], subSlot(slot, \"signal:server\"));\n return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot, subSlot(slot, \"signal:value\"));\n}\nexport { createAutoImportApis } from \"./auto-import-api.js\";\n"],"names":["ScopeContext","createContext","ScopeProvider","scope","children","createElement","useScopeContext","useContext","allImportApis","createAutoImportApis","prefix","generateBatchAutoImports","useSignal","source","slot","useSyncExternalStore","useCallback","notify","unsubscribe","dispose","effect","subSlot"],"mappings":"gQAQO,IAAMA,EAAeC,EAA4B,MAEjD,SAASC,EAAc,CAAEC,MAAAA,CAAK,CAAEC,SAAAA,CAAQ,CAAsB,EACnE,OAAOC,EAAcL,EAAc,CAAE,MAAOG,EAAOC,SAAAA,CAAS,EAC9D,CAEO,SAASE,IACd,OAAOC,EAAWP,EACpB,CCdA,IAAMQ,EAA0B,CAAC,eAAgB,gBAAiB,kBAAmB,YAAY,CAEpFC,EAAuB,CAACC,EAAiB,EAAE,GAC/CC,EAAyB,CAC9B,CACED,OAAAA,EACA,SAAU,eACV,QAASF,CACX,EACD,ECFI,SAASI,EAAaC,CAAuB,CAAEC,CAAa,EACjE,IAAMX,EAAQG,IAkBd,OAAOS,EAjBWC,EAChB,AAACC,IACC,IAAMC,EAAcL,EAAO,SAAS,CAACI,GAE/BE,EAAUC,EAAO,KACrBP,EAAO,GAAG,EACZ,GACA,MAAO,KACLK,IACAC,GACF,CACF,EACA,CAACN,EAAO,CACRQ,EAAQP,EAAM,qBAEIE,EAAY,IAAMH,EAAO,GAAG,GAAI,CAACA,EAAO,CAAEQ,EAAQP,EAAM,gBAClDE,EAAY,IAAOb,EAAQA,EAAM,GAAG,CAACU,GAAUA,EAAO,GAAG,GAAK,CAACV,EAAOU,EAAO,CAAEQ,EAAQP,EAAM,kBAChDO,EAAQP,EAAM,gBACvF,Q"}
|
package/dist/proxy.d.ts
ADDED
package/dist/proxy.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{useCallback as r,useSyncExternalStore as o}from"octane";import{snapshot as p,subscribe as t}from"@slux/proxy";import{subSlot as e}from"./5.js";function m(m,u){let x=r(r=>t(m,r),[m],e(u,"proxy:subscribe")),i=r(()=>p(m),[m],e(u,"proxy:read"));return o(x,i,i,e(u,"proxy:value"))}export{m as useSnapshot};
|
|
2
|
+
//# sourceMappingURL=proxy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy.js","sources":["../src/proxy.ts"],"sourcesContent":["import { useCallback, useSyncExternalStore } from \"octane\";\nimport { snapshot, subscribe } from \"@slux/proxy\";\nimport type { Snapshot } from \"@slux/proxy\";\nimport { subSlot } from \"./slots.js\";\n\nexport function useSnapshot<T extends object>(proxy: T, slot?: symbol): Snapshot<T> {\n const sub = useCallback((notify: () => void) => subscribe(proxy, notify), [proxy], subSlot(slot, \"proxy:subscribe\"));\n const getSnapshot = useCallback(() => snapshot(proxy), [proxy], subSlot(slot, \"proxy:read\"));\n return useSyncExternalStore(sub, getSnapshot, getSnapshot, subSlot(slot, \"proxy:value\"));\n}\n"],"names":["useSnapshot","proxy","slot","sub","useCallback","notify","subscribe","subSlot","getSnapshot","snapshot","useSyncExternalStore"],"mappings":"oKAKO,SAASA,EAA8BC,CAAQ,CAAEC,CAAa,EACnE,IAAMC,EAAMC,EAAY,AAACC,GAAuBC,EAAUL,EAAOI,GAAS,CAACJ,EAAM,CAAEM,EAAQL,EAAM,oBAC3FM,EAAcJ,EAAY,IAAMK,EAASR,GAAQ,CAACA,EAAM,CAAEM,EAAQL,EAAM,eAC9E,OAAOQ,EAAqBP,EAAKK,EAAaA,EAAaD,EAAQL,EAAM,eAC3E,Q"}
|
package/dist/rxjs.d.ts
ADDED
package/dist/rxjs.js
ADDED
package/dist/rxjs.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rxjs.js","sources":["../src/rxjs.ts"],"sourcesContent":["import type { ObservableState } from \"@slux/rxjs\";\nimport { useSignal } from \"./index.js\";\n\nexport function useObservable<T>(source: ObservableState<T>, slot?: symbol): T {\n return useSignal(source, slot);\n}\n"],"names":["useObservable","source","slot","useSignal"],"mappings":"iDAGO,SAASA,EAAiBC,CAA0B,CAAEC,CAAa,EACxE,OAAOC,EAAUF,EAAQC,EAC3B,Q"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { MachineSnapshot } from '@slux/state-machine';
|
|
2
|
+
import type { StateMachine } from '@slux/state-machine';
|
|
3
|
+
|
|
4
|
+
export declare function useMachine<Context, Event extends {
|
|
5
|
+
type: string;
|
|
6
|
+
}, State extends string>(machine: StateMachine<Context, Event, State>, slot?: symbol): MachineSnapshot<Context, State>;
|
|
7
|
+
|
|
8
|
+
export { }
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state-machine.js","sources":["../src/state-machine.ts"],"sourcesContent":["import type { MachineSnapshot, StateMachine } from \"@slux/state-machine\";\nimport { useSignal } from \"./index.js\";\n\nexport function useMachine<Context, Event extends { type: string }, State extends string>(\n machine: StateMachine<Context, Event, State>,\n slot?: symbol,\n): MachineSnapshot<Context, State> {\n return useSignal(machine, slot);\n}\n"],"names":["useMachine","machine","slot","useSignal"],"mappings":"iDAGO,SAASA,EACdC,CAA4C,CAC5CC,CAAa,EAEb,OAAOC,EAAUF,EAASC,EAC5B,Q"}
|
package/dist/store.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { StoreApi } from '@slux/store';
|
|
2
|
+
|
|
3
|
+
export declare function useStore<State extends object>(store: StoreApi<State>, slot?: symbol): State;
|
|
4
|
+
|
|
5
|
+
export declare function useStore<State extends object, Selection>(store: StoreApi<State>, selector: (state: State) => Selection, equalityFn?: (previous: Selection, next: Selection) => boolean, slot?: symbol): Selection;
|
|
6
|
+
|
|
7
|
+
export { }
|
package/dist/store.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{useCallback as e,useMemo as t,useSyncExternalStore as o}from"octane";import{subSlot as r}from"./5.js";function s(s,i,n,c){let u="symbol"==typeof i?i:"symbol"==typeof n?n:c,f="function"==typeof i?i:void 0,l="function"==typeof n?n:Object.is,b=e(e=>s.subscribe(e),[s],r(u,"store:subscribe")),p=t(()=>{let e;return f?()=>{let t=s.getState();if(e&&Object.is(e.state,t))return e.selection;let o=f(t),r=e&&l(e.selection,o)?e.selection:o;return e={state:t,selection:r},r}:()=>s.getState()},[s,f,l],r(u,"store:read"));return o(b,p,p,r(u,"store:value"))}export{s as useStore};
|
|
2
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sources":["../src/store.ts"],"sourcesContent":["import { useCallback, useMemo, useSyncExternalStore } from \"octane\";\nimport type { StoreApi } from \"@slux/store\";\nimport { subSlot } from \"./slots.js\";\n\nexport function useStore<State extends object>(store: StoreApi<State>, slot?: symbol): State;\nexport function useStore<State extends object, Selection>(\n store: StoreApi<State>,\n selector: (state: State) => Selection,\n equalityFn?: (previous: Selection, next: Selection) => boolean,\n slot?: symbol,\n): Selection;\nexport function useStore<State extends object, Selection>(\n store: StoreApi<State>,\n selectorOrSlot?: ((state: State) => Selection) | symbol,\n equalityOrSlot?: ((previous: Selection, next: Selection) => boolean) | symbol,\n explicitSlot?: symbol,\n): State | Selection {\n const slot = typeof selectorOrSlot === \"symbol\" ? selectorOrSlot : typeof equalityOrSlot === \"symbol\" ? equalityOrSlot : explicitSlot;\n const selector = typeof selectorOrSlot === \"function\" ? selectorOrSlot : undefined;\n const equalityFn = typeof equalityOrSlot === \"function\" ? equalityOrSlot : Object.is;\n const subscribe = useCallback((notify: () => void) => store.subscribe(notify), [store], subSlot(slot, \"store:subscribe\"));\n const getSnapshot = useMemo(\n () => {\n if (!selector) return () => store.getState();\n let cache: { state: State; selection: Selection } | undefined;\n return () => {\n const state = store.getState();\n if (cache && Object.is(cache.state, state)) return cache.selection;\n const next = selector(state);\n const selection = cache && equalityFn(cache.selection, next) ? cache.selection : next;\n cache = { state, selection };\n return selection;\n };\n },\n [store, selector, equalityFn],\n subSlot(slot, \"store:read\"),\n );\n return useSyncExternalStore<State | Selection>(subscribe, getSnapshot, getSnapshot, subSlot(slot, \"store:value\"));\n}\n"],"names":["useStore","store","selectorOrSlot","equalityOrSlot","explicitSlot","slot","selector","undefined","equalityFn","Object","subscribe","useCallback","notify","subSlot","getSnapshot","useMemo","cache","state","next","selection","useSyncExternalStore"],"mappings":"2HAWO,SAASA,EACdC,CAAsB,CACtBC,CAAuD,CACvDC,CAA6E,CAC7EC,CAAqB,EAErB,IAAMC,EAAO,AAA0B,UAA1B,OAAOH,EAA8BA,EAAiB,AAA0B,UAA1B,OAAOC,EAA8BA,EAAiBC,EACnHE,EAAW,AAA0B,YAA1B,OAAOJ,EAAgCA,EAAiBK,OACnEC,EAAa,AAA0B,YAA1B,OAAOL,EAAgCA,EAAiBM,OAAO,EAAE,CAC9EC,EAAYC,EAAY,AAACC,GAAuBX,EAAM,SAAS,CAACW,GAAS,CAACX,EAAM,CAAEY,EAAQR,EAAM,oBAChGS,EAAcC,EAClB,SAEMC,SADJ,AAAKV,EAEE,KACL,IAAMW,EAAQhB,EAAM,QAAQ,GAC5B,GAAIe,GAASP,OAAO,EAAE,CAACO,EAAM,KAAK,CAAEC,GAAQ,OAAOD,EAAM,SAAS,CAClE,IAAME,EAAOZ,EAASW,GAChBE,EAAYH,GAASR,EAAWQ,EAAM,SAAS,CAAEE,GAAQF,EAAM,SAAS,CAAGE,EAEjF,OADAF,EAAQ,CAAEC,MAAAA,EAAOE,UAAAA,CAAU,EACpBA,CACT,EATsB,IAAMlB,EAAM,QAAQ,EAU5C,EACA,CAACA,EAAOK,EAAUE,EAAW,CAC7BK,EAAQR,EAAM,eAEhB,OAAOe,EAAwCV,EAAWI,EAAaA,EAAaD,EAAQR,EAAM,eACpG,Q"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,107 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@slux/octane",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "slux octane bindings",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"slux",
|
|
7
|
+
"octane",
|
|
8
|
+
"bindings",
|
|
9
|
+
"state-management"
|
|
10
|
+
],
|
|
11
|
+
"type": "module",
|
|
12
|
+
"sideEffects": false,
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"author": "itmanyong <itmanyong@gmail.com>",
|
|
15
|
+
"publishConfig": {
|
|
16
|
+
"access": "public"
|
|
17
|
+
},
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git+https://github.com/itmanyong/slux.git",
|
|
21
|
+
"directory": "packages/octane"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"src",
|
|
25
|
+
"dist",
|
|
26
|
+
"README.md",
|
|
27
|
+
"CHANGELOG.md",
|
|
28
|
+
"package.json"
|
|
29
|
+
],
|
|
30
|
+
"main": "./src/index.ts",
|
|
31
|
+
"module": "./src/index.ts",
|
|
32
|
+
"types": "./src/index.ts",
|
|
33
|
+
"exports": {
|
|
34
|
+
".": "./src/index.ts",
|
|
35
|
+
"./atom": "./src/atom.ts",
|
|
36
|
+
"./store": "./src/store.ts",
|
|
37
|
+
"./proxy": "./src/proxy.ts",
|
|
38
|
+
"./package.json": "./package.json",
|
|
39
|
+
"./flux": "./src/flux.ts",
|
|
40
|
+
"./state-machine": "./src/state-machine.ts",
|
|
41
|
+
"./rxjs": "./src/rxjs.ts"
|
|
42
|
+
},
|
|
43
|
+
"peerDependencies": {
|
|
44
|
+
"@slux/atom": "^0.1.1",
|
|
45
|
+
"@slux/core": "^0.1.1",
|
|
46
|
+
"@slux/proxy": "^0.1.1",
|
|
47
|
+
"@slux/store": "^0.1.1",
|
|
48
|
+
"octane": ">=0.16.0",
|
|
49
|
+
"@slux/flux": "^0.1.1",
|
|
50
|
+
"@slux/state-machine": "^0.1.1",
|
|
51
|
+
"@slux/rxjs": "^0.1.1"
|
|
52
|
+
},
|
|
53
|
+
"peerDependenciesMeta": {
|
|
54
|
+
"@slux/atom": {
|
|
55
|
+
"optional": true
|
|
56
|
+
},
|
|
57
|
+
"@slux/proxy": {
|
|
58
|
+
"optional": true
|
|
59
|
+
},
|
|
60
|
+
"@slux/store": {
|
|
61
|
+
"optional": true
|
|
62
|
+
},
|
|
63
|
+
"@slux/flux": {
|
|
64
|
+
"optional": true
|
|
65
|
+
},
|
|
66
|
+
"@slux/state-machine": {
|
|
67
|
+
"optional": true
|
|
68
|
+
},
|
|
69
|
+
"@slux/rxjs": {
|
|
70
|
+
"optional": true
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
"devDependencies": {
|
|
74
|
+
"@slux/atom": "0.1.1",
|
|
75
|
+
"@slux/core": "0.1.1",
|
|
76
|
+
"@slux/proxy": "0.1.1",
|
|
77
|
+
"@slux/store": "0.1.1",
|
|
78
|
+
"jsdom": "30.1.3",
|
|
79
|
+
"rstack": "0.9.0",
|
|
80
|
+
"typescript": "7.1.0-beta",
|
|
81
|
+
"octane": "0.16.7",
|
|
82
|
+
"@tsrx/oxc": "0.23.0",
|
|
83
|
+
"@slux/flux": "0.1.1",
|
|
84
|
+
"@slux/state-machine": "0.1.1",
|
|
85
|
+
"@slux/rxjs": "0.1.1",
|
|
86
|
+
"rxjs": "7.8.2"
|
|
87
|
+
},
|
|
88
|
+
"octane": {
|
|
89
|
+
"hookSlots": {
|
|
90
|
+
"manual": [
|
|
91
|
+
"src"
|
|
92
|
+
]
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
"dependencies": {
|
|
96
|
+
"@slux/shared": "0.1.1"
|
|
97
|
+
},
|
|
98
|
+
"scripts": {
|
|
99
|
+
"dev": "rs lib -w",
|
|
100
|
+
"build": "rs lib",
|
|
101
|
+
"test": "rs test",
|
|
102
|
+
"lint": "rs lint",
|
|
103
|
+
"fmt": "rs fmt",
|
|
104
|
+
"typecheck": "tsc --noEmit",
|
|
105
|
+
"clean": "rm -rf dist node_modules .turbo"
|
|
106
|
+
}
|
|
6
107
|
}
|
package/src/atom.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { useCallback, useSyncExternalStore } from "octane";
|
|
2
|
+
import type { Atom, AnyReadableAtom } from "@slux/atom";
|
|
3
|
+
import { subSlot } from "./slots.js";
|
|
4
|
+
|
|
5
|
+
export function useAtom<T>(atom: Atom<T>, slot?: symbol): [T, (value: T) => void] {
|
|
6
|
+
return [useAtomValue(atom, subSlot(slot, "atom:value")), useSetAtom(atom, subSlot(slot, "atom:setter"))];
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function useAtomValue<T>(atom: AnyReadableAtom<T>, slot?: symbol): T {
|
|
10
|
+
const subscribe = useCallback((notify: () => void) => atom.subscribe(notify), [atom], subSlot(slot, "atom:subscribe"));
|
|
11
|
+
const getSnapshot = useCallback(() => atom.get(), [atom], subSlot(slot, "atom:read"));
|
|
12
|
+
return useSyncExternalStore(subscribe, getSnapshot, getSnapshot, subSlot(slot, "atom:store"));
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function useSetAtom<T>(atom: Atom<T>, slot?: symbol): (value: T) => void {
|
|
16
|
+
return useCallback((value: T) => atom.set(value), [atom], subSlot(slot, "atom:write"));
|
|
17
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { generateBatchAutoImports } from "@slux/shared";
|
|
2
|
+
|
|
3
|
+
const allImportApis: string[] = ["ScopeContext", "ScopeProvider", "useScopeContext", "useSignal"];
|
|
4
|
+
|
|
5
|
+
export const createAutoImportApis = (prefix: string = "") => {
|
|
6
|
+
return generateBatchAutoImports([
|
|
7
|
+
{
|
|
8
|
+
prefix,
|
|
9
|
+
fromName: "@slux/octane",
|
|
10
|
+
imports: allImportApis,
|
|
11
|
+
},
|
|
12
|
+
]);
|
|
13
|
+
};
|
package/src/flux.ts
ADDED
package/src/index.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { useCallback, useSyncExternalStore } from "octane";
|
|
2
|
+
import { effect } from "@slux/core";
|
|
3
|
+
import type { Subscribable } from "@slux/core";
|
|
4
|
+
import { useScopeContext } from "./scope.js";
|
|
5
|
+
import { subSlot } from "./slots.js";
|
|
6
|
+
|
|
7
|
+
export { ScopeContext, ScopeProvider, useScopeContext } from "./scope.js";
|
|
8
|
+
export type { ScopeProviderProps } from "./scope.js";
|
|
9
|
+
|
|
10
|
+
export function useSignal<T>(source: Subscribable<T>, slot?: symbol): T {
|
|
11
|
+
const scope = useScopeContext();
|
|
12
|
+
const subscribe = useCallback(
|
|
13
|
+
(notify: () => void) => {
|
|
14
|
+
const unsubscribe = source.subscribe(notify);
|
|
15
|
+
// core computed 按读取刷新,保持 effect 才能持续接收依赖变化。
|
|
16
|
+
const dispose = effect(() => {
|
|
17
|
+
source.get();
|
|
18
|
+
});
|
|
19
|
+
return () => {
|
|
20
|
+
unsubscribe();
|
|
21
|
+
dispose();
|
|
22
|
+
};
|
|
23
|
+
},
|
|
24
|
+
[source],
|
|
25
|
+
subSlot(slot, "signal:subscribe"),
|
|
26
|
+
);
|
|
27
|
+
const getSnapshot = useCallback(() => source.get(), [source], subSlot(slot, "signal:read"));
|
|
28
|
+
const getServerSnapshot = useCallback(() => (scope ? scope.get(source) : source.get()), [scope, source], subSlot(slot, "signal:server"));
|
|
29
|
+
return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot, subSlot(slot, "signal:value"));
|
|
30
|
+
}
|
|
31
|
+
export { createAutoImportApis } from "./auto-import-api.js";
|
package/src/proxy.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { useCallback, useSyncExternalStore } from "octane";
|
|
2
|
+
import { snapshot, subscribe } from "@slux/proxy";
|
|
3
|
+
import type { Snapshot } from "@slux/proxy";
|
|
4
|
+
import { subSlot } from "./slots.js";
|
|
5
|
+
|
|
6
|
+
export function useSnapshot<T extends object>(proxy: T, slot?: symbol): Snapshot<T> {
|
|
7
|
+
const sub = useCallback((notify: () => void) => subscribe(proxy, notify), [proxy], subSlot(slot, "proxy:subscribe"));
|
|
8
|
+
const getSnapshot = useCallback(() => snapshot(proxy), [proxy], subSlot(slot, "proxy:read"));
|
|
9
|
+
return useSyncExternalStore(sub, getSnapshot, getSnapshot, subSlot(slot, "proxy:value"));
|
|
10
|
+
}
|
package/src/rxjs.ts
ADDED
package/src/scope.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { createContext, createElement, useContext } from "octane";
|
|
2
|
+
import type { OctaneNode } from "octane";
|
|
3
|
+
import type { Scope } from "@slux/core";
|
|
4
|
+
|
|
5
|
+
export type ScopeProviderProps = {
|
|
6
|
+
readonly scope: Scope;
|
|
7
|
+
readonly children?: OctaneNode;
|
|
8
|
+
};
|
|
9
|
+
export const ScopeContext = createContext<Scope | null>(null);
|
|
10
|
+
|
|
11
|
+
export function ScopeProvider({ scope, children }: ScopeProviderProps) {
|
|
12
|
+
return createElement(ScopeContext, { value: scope, children });
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function useScopeContext(): Scope | null {
|
|
16
|
+
return useContext(ScopeContext);
|
|
17
|
+
}
|
package/src/slots.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { MachineSnapshot, StateMachine } from "@slux/state-machine";
|
|
2
|
+
import { useSignal } from "./index.js";
|
|
3
|
+
|
|
4
|
+
export function useMachine<Context, Event extends { type: string }, State extends string>(
|
|
5
|
+
machine: StateMachine<Context, Event, State>,
|
|
6
|
+
slot?: symbol,
|
|
7
|
+
): MachineSnapshot<Context, State> {
|
|
8
|
+
return useSignal(machine, slot);
|
|
9
|
+
}
|
package/src/store.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { useCallback, useMemo, useSyncExternalStore } from "octane";
|
|
2
|
+
import type { StoreApi } from "@slux/store";
|
|
3
|
+
import { subSlot } from "./slots.js";
|
|
4
|
+
|
|
5
|
+
export function useStore<State extends object>(store: StoreApi<State>, slot?: symbol): State;
|
|
6
|
+
export function useStore<State extends object, Selection>(
|
|
7
|
+
store: StoreApi<State>,
|
|
8
|
+
selector: (state: State) => Selection,
|
|
9
|
+
equalityFn?: (previous: Selection, next: Selection) => boolean,
|
|
10
|
+
slot?: symbol,
|
|
11
|
+
): Selection;
|
|
12
|
+
export function useStore<State extends object, Selection>(
|
|
13
|
+
store: StoreApi<State>,
|
|
14
|
+
selectorOrSlot?: ((state: State) => Selection) | symbol,
|
|
15
|
+
equalityOrSlot?: ((previous: Selection, next: Selection) => boolean) | symbol,
|
|
16
|
+
explicitSlot?: symbol,
|
|
17
|
+
): State | Selection {
|
|
18
|
+
const slot = typeof selectorOrSlot === "symbol" ? selectorOrSlot : typeof equalityOrSlot === "symbol" ? equalityOrSlot : explicitSlot;
|
|
19
|
+
const selector = typeof selectorOrSlot === "function" ? selectorOrSlot : undefined;
|
|
20
|
+
const equalityFn = typeof equalityOrSlot === "function" ? equalityOrSlot : Object.is;
|
|
21
|
+
const subscribe = useCallback((notify: () => void) => store.subscribe(notify), [store], subSlot(slot, "store:subscribe"));
|
|
22
|
+
const getSnapshot = useMemo(
|
|
23
|
+
() => {
|
|
24
|
+
if (!selector) return () => store.getState();
|
|
25
|
+
let cache: { state: State; selection: Selection } | undefined;
|
|
26
|
+
return () => {
|
|
27
|
+
const state = store.getState();
|
|
28
|
+
if (cache && Object.is(cache.state, state)) return cache.selection;
|
|
29
|
+
const next = selector(state);
|
|
30
|
+
const selection = cache && equalityFn(cache.selection, next) ? cache.selection : next;
|
|
31
|
+
cache = { state, selection };
|
|
32
|
+
return selection;
|
|
33
|
+
};
|
|
34
|
+
},
|
|
35
|
+
[store, selector, equalityFn],
|
|
36
|
+
subSlot(slot, "store:read"),
|
|
37
|
+
);
|
|
38
|
+
return useSyncExternalStore<State | Selection>(subscribe, getSnapshot, getSnapshot, subSlot(slot, "store:value"));
|
|
39
|
+
}
|