@slux/tab-sync 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 ADDED
@@ -0,0 +1,33 @@
1
+ # @slux/tab-sync
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/shared@0.1.1
16
+ - @slux/store@0.1.1
17
+
18
+ ## 0.1.0
19
+
20
+ ### Minor Changes
21
+
22
+ - 首次公开发布 slux 的 25 个包,统一版本为 0.1.0。
23
+
24
+ - 提供框架无关的响应式核心与共享工具,以及 Store、Atom、Proxy、Flux、状态机和 RxJS 六种状态组织方式。
25
+ - 提供持久化、历史记录、跨标签页同步、遥测和开发工具五类 Store 扩展。
26
+ - 提供 React、Vue、Solid、Svelte、Angular、Preact、Ripple、Inferno、Octane、Vitarx、ActView 和 Viewfly 绑定。
27
+ - 提供各包文档与对应示例,并接入发布检查和文档部署工作流。
28
+
29
+ ### Patch Changes
30
+
31
+ - Updated dependencies
32
+ - @slux/shared@0.1.0
33
+ - @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,205 @@
1
- # Temporary Holding Version
1
+ # @slux/tab-sync
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ 为 `@slux/store` 提供基于 `BroadcastChannel` 的跨标签页状态同步。`broadcast` 是一个标准 `Middleware<T>`:把本地后续变更广播到同名通道,并把收到的远端消息经完整中间件链应用。它作为 `@slux/store` 中间件接入,复用 Store 的公共契约,本文只写本包差异。
4
+
5
+ > 中间件的组合方式、实例归属、销毁清理链与顺序等通用约定见仓库根 [README](../../README.md) 的「Store 中间件 · 通用契约」与「本地开发」。
6
+
7
+ ## 特性与适用场景
8
+
9
+ | 场景 | 是否适合 | 说明 |
10
+ | -------------------------------------- | -------- | -------------------------------------------- |
11
+ | 多标签共享主题、登录标记、草稿计数 | 适合 | 同名 `channel` 的实例互相同步后续变更 |
12
+ | 需要按字段裁剪发送内容 | 适合 | `filter` 从更新后的完整状态选择数据字段 |
13
+ | 需要处理乱序/旧消息 | 适合 | 可选 `timestamp` 或自定义 `ConflictResolver` |
14
+ | 无 `BroadcastChannel` 的环境(含 SSR) | 适合 | 降级为纯本地更新,不抛错 |
15
+ | 需要新标签页自动获得已有状态 | 不适合 | 不同步初始状态,只同步之后的变更 |
16
+ | 状态含 action 函数 | 需处理 | 结构化克隆不支持函数,需用 `filter` 剔除 |
17
+ | 需要跨设备、跨网络同步 | 不适合 | 仅限同一来源的多个标签/上下文 |
18
+ | 需要强一致、无丢更新的合并 | 需谨慎 | 默认 `last-write-wins` 按到达顺序覆盖 |
19
+
20
+ ## 安装
21
+
22
+ ```sh
23
+ pnpm add @slux/store @slux/tab-sync
24
+ ```
25
+
26
+ - **运行时依赖**(`dependencies`):`@slux/store`、`@slux/shared`。
27
+ - **无 `peerDependencies`**:示例里直接 import 的 `@slux/store` 建议在应用侧显式声明。
28
+ - **宿主能力**:`BroadcastChannel` 由浏览器 / Worker / 部分 Node 环境提供,不是 npm 依赖;`crypto.randomUUID` 缺失时源码会回退到随机字符串生成 `senderId`。
29
+
30
+ ## 快速开始
31
+
32
+ ```ts
33
+ import { createStore } from "@slux/store";
34
+ import { broadcast } from "@slux/tab-sync";
35
+
36
+ type State = { theme: string; count: number; increment: () => void };
37
+
38
+ const store = createStore<State>(
39
+ (set) => ({
40
+ theme: "light",
41
+ count: 0,
42
+ increment: () => set((state) => ({ count: state.count + 1 })),
43
+ }),
44
+ {
45
+ middleware: [
46
+ broadcast<State>({
47
+ channel: "app-state",
48
+ // 只发送数据字段,action 函数无法通过结构化克隆
49
+ filter: (state) => ({ theme: state.theme, count: state.count }),
50
+ }),
51
+ ],
52
+ },
53
+ );
54
+
55
+ store.setState({ theme: "dark" });
56
+ ```
57
+
58
+ ## 核心 API
59
+
60
+ ### `broadcast<T extends object>(options: BroadcastOptions<T>): Middleware<T>`
61
+
62
+ | 选项 | 类型 | 默认值 | 说明 |
63
+ | -------------------- | --------------------------------------------------------- | ------------------- | ------------------------------------------------------ |
64
+ | `channel` | `string` | 必填 | 通道名称;不同名称互相隔离 |
65
+ | `filter` | `(state: T) => Partial<T>` | 无 | 从更新后的完整状态选择发送字段;只控制发送,不过滤接收 |
66
+ | `conflictResolution` | `"last-write-wins" \| "timestamp" \| ConflictResolver<T>` | `"last-write-wins"` | 远端消息的接受与合并策略 |
67
+
68
+ 传输格式为 `BroadcastMessage<T>`:`{ readonly senderId: string; readonly timestamp: number; readonly state: Partial<T> }`。`timestamp` 由 `Date.now()` 生成。
69
+
70
+ | 冲突策略 | 行为 |
71
+ | ------------------------- | ---------------------------------------------------------------------------------------- |
72
+ | `last-write-wins`(默认) | 按消息到达顺序应用,不比较 `timestamp` |
73
+ | `timestamp` | 只接受严格晚于「最近一次已接受远端消息」时间戳的消息;初始阈值为 `0`,本地更新不推进该值 |
74
+ | `ConflictResolver<T>` | `(incoming: Partial<T>, current: T) => Partial<T>`;返回值经完整中间件链应用 |
75
+
76
+ `timestamp` 依赖各端时钟,不能解决时钟偏差;自定义解析器可以读取本地当前状态后决定实际应用哪些字段。
77
+
78
+ ## 专属语义与边界
79
+
80
+ **`filter` 的取向**:无 `filter` 时发送的是本次 `onSet` 的 `partial`(部分状态);提供 `filter` 时,源码对**更新后的完整状态** `api.getState()` 调用它,并用返回值作为 `state`。`filter` 只影响发送,接收端不做字段过滤。
81
+
82
+ **BroadcastChannel 依赖与降级**:`init` 时若 `typeof BroadcastChannel === "undefined"` 直接返回,不建立通道,本地写入照常执行(SSR 安全)。`crypto.randomUUID` 缺失时仍会生成不同的 `senderId`。
83
+
84
+ **结构化克隆限制**:`postMessage` 的数据必须支持结构化克隆。状态含 action 函数、Symbol、DOM 节点时,若未通过 `filter` 剔除,会由原生通道抛错(`DataCloneError`);浏览器的构造/发送错误会正常抛出,本包不做静默重试。
85
+
86
+ **忽略自身与回传**:入口忽略 `senderId` 等于自身的消息;应用远端状态期间用 `applyingRemote` 守卫,避免把远端更新再次广播回去形成循环。守卫在 `finally` 中复位,因此业务订阅者抛错也不会让后续本地更新永久停止广播(但远端应用本身抛出的异常仍会向调用者传播)。
87
+
88
+ **消息校验**:入口检查 `senderId` 为字符串、`timestamp` 为有限数字、`state` 为非 `null` 非数组的普通对象;不满足的消息直接忽略,不做业务字段的运行时 schema 校验。
89
+
90
+ **不拉取初始状态**:新建标签页不会自动获得其他标签页的当前状态,只同步创建之后的写入。需要初始值时用 `@slux/storage` 恢复,或在业务层做一次握手。
91
+
92
+ **多标签冲突与顺序**:`last-write-wins` 下后到的消息覆盖先到的,即使其 `timestamp` 更小;`timestamp` 下早于或等于最近已接受远端时间戳的消息被丢弃;自定义解析器自行决定合并。并发写入没有全局仲裁。
93
+
94
+ **时间旅行**:撤销/重做经过完整中间件链,因此也会被广播;仓库中 history 包的 `__tests__/integration.test.ts` 含 `broadcast` 与 `history` 组合的用例。
95
+
96
+ **销毁**:`onDestroy` 把 `onmessage` 置空、`close()` 通道、复位时间戳阈值;`destroy()` 可重复调用。已经发出的消息无法撤回。
97
+
98
+ ## 自动导入
99
+
100
+ `createAutoImportApis(prefix?)` 通过 `@slux/shared` 的 `generateBatchAutoImports` 生成 `unplugin-auto-import` 的 `ImportsMap`,来源名为 `@slux/tab-sync`。登记清单以 `src/auto-import-api.ts` 为准,只有 1 项:`broadcast`。
101
+
102
+ ```ts
103
+ import AutoImport from "unplugin-auto-import/vite";
104
+ import { createAutoImportApis } from "@slux/tab-sync";
105
+
106
+ export default {
107
+ plugins: [AutoImport({ imports: [createAutoImportApis()] })],
108
+ };
109
+ ```
110
+
111
+ 传入字符串前缀会为登记项生成带前缀别名(如 `createAutoImportApis("u")` 把 `broadcast` 映射为 `uBroadcast`);前缀以小写字母结尾时 API 名首字母大写,否则直接拼接。`createAutoImportApis` 本身不登记。
112
+
113
+ ## 类型导出
114
+
115
+ `src/index.ts` 实际导出的类型:`BroadcastMessage<T>`、`BroadcastOptions<T>`、`ConflictResolution<T>`、`ConflictResolver<T>`。
116
+
117
+ 本包不提供额外的控制器方法或替代 transport API;返回值就是 `@slux/store` 的 `Middleware<T>`,该类型由 `@slux/store` 定义,本包不重复导出。
118
+
119
+ ## 不适用的场景
120
+
121
+ - 需要同步初始状态或全量快照:本包只广播后续变更。
122
+ - 运行环境没有 `BroadcastChannel` 且必须跨进程同步:会降级为纯本地更新。
123
+ - 需要跨设备、跨网络或服务端多实例同步:应使用后端通道。
124
+ - 需要强一致、无丢更新的合并:默认 `last-write-wins` 可能覆盖较新的写入,需自定义 `ConflictResolver`。
125
+ - payload 含函数、`Symbol` 等不可结构化克隆的值:需 `filter` 只选数据字段,否则原生通道抛错。
126
+ - 只需要单标签持久化:使用 `@slux/storage`。
127
+
128
+ ## 版本、许可与反馈
129
+
130
+ | 项 | 值 | 来源 |
131
+ | -------- | --------------------------------------------------------- | ----------------------------------------- |
132
+ | 版本 | `0.1.0` | `package.json` |
133
+ | 许可 | MIT | `package.json` / [LICENSE](../../LICENSE) |
134
+ | 问题反馈 | [GitHub Issues](https://github.com/itmanyong/slux/issues) | `package.json` `bugs` |
135
+
136
+ > `pnpm add` 基于 `package.json` 的发布配置;是否已实际发布到 npm 及线上版本号无法从源码确认。
137
+
138
+ <!-- package-navigation:start -->
139
+
140
+ ## 包导航
141
+
142
+ ### 核心层
143
+
144
+ 响应式运行时与零依赖工具,其余各层都建立在 core 之上。
145
+
146
+ | 包名 | 版本 | 在线文档 | 功能 |
147
+ | ----------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ | ---------------------- |
148
+ | [`@slux/core`](https://github.com/itmanyong/slux/tree/main/packages/core) | [![npm version](https://img.shields.io/npm/v/@slux/core)](https://www.npmjs.com/package/@slux/core) [![npmx version](https://npmx.dev/api/registry/badge/version/@slux/core?label=npmx)](https://npmx.dev/package/@slux/core) | [在线文档](https://itmanyong.github.io/slux/packages/core) | 框架无关的响应式运行时 |
149
+ | [`@slux/shared`](https://github.com/itmanyong/slux/tree/main/packages/shared) | [![npm version](https://img.shields.io/npm/v/@slux/shared)](https://www.npmjs.com/package/@slux/shared) [![npmx version](https://npmx.dev/api/registry/badge/version/@slux/shared?label=npmx)](https://npmx.dev/package/@slux/shared) | [在线文档](https://itmanyong.github.io/slux/packages/shared) | 零依赖的共享工具 |
150
+
151
+ ### 范式层
152
+
153
+ 六种组织状态的方式,共享 core 的运行时,可在同一个依赖图中混用。
154
+
155
+ | 包名 | 版本 | 在线文档 | 功能 |
156
+ | ------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- | ---------------------------------- |
157
+ | [`@slux/store`](https://github.com/itmanyong/slux/tree/main/packages/store) | [![npm version](https://img.shields.io/npm/v/@slux/store)](https://www.npmjs.com/package/@slux/store) [![npmx version](https://npmx.dev/api/registry/badge/version/@slux/store?label=npmx)](https://npmx.dev/package/@slux/store) | [在线文档](https://itmanyong.github.io/slux/packages/store) | 状态与 action 集中管理,支持中间件 |
158
+ | [`@slux/atom`](https://github.com/itmanyong/slux/tree/main/packages/atom) | [![npm version](https://img.shields.io/npm/v/@slux/atom)](https://www.npmjs.com/package/@slux/atom) [![npmx version](https://npmx.dev/api/registry/badge/version/@slux/atom?label=npmx)](https://npmx.dev/package/@slux/atom) | [在线文档](https://itmanyong.github.io/slux/packages/atom) | 细粒度原子状态与派生 |
159
+ | [`@slux/proxy`](https://github.com/itmanyong/slux/tree/main/packages/proxy) | [![npm version](https://img.shields.io/npm/v/@slux/proxy)](https://www.npmjs.com/package/@slux/proxy) [![npmx version](https://npmx.dev/api/registry/badge/version/@slux/proxy?label=npmx)](https://npmx.dev/package/@slux/proxy) | [在线文档](https://itmanyong.github.io/slux/packages/proxy) | 可变代理写入与只读快照 |
160
+ | [`@slux/flux`](https://github.com/itmanyong/slux/tree/main/packages/flux) | [![npm version](https://img.shields.io/npm/v/@slux/flux)](https://www.npmjs.com/package/@slux/flux) [![npmx version](https://npmx.dev/api/registry/badge/version/@slux/flux?label=npmx)](https://npmx.dev/package/@slux/flux) | [在线文档](https://itmanyong.github.io/slux/packages/flux) | reducer + dispatch 范式 |
161
+ | [`@slux/state-machine`](https://github.com/itmanyong/slux/tree/main/packages/state-machine) | [![npm version](https://img.shields.io/npm/v/@slux/state-machine)](https://www.npmjs.com/package/@slux/state-machine) [![npmx version](https://npmx.dev/api/registry/badge/version/@slux/state-machine?label=npmx)](https://npmx.dev/package/@slux/state-machine) | [在线文档](https://itmanyong.github.io/slux/packages/state-machine) | 平面有限状态机 |
162
+ | [`@slux/rxjs`](https://github.com/itmanyong/slux/tree/main/packages/rxjs) | [![npm version](https://img.shields.io/npm/v/@slux/rxjs)](https://www.npmjs.com/package/@slux/rxjs) [![npmx version](https://npmx.dev/api/registry/badge/version/@slux/rxjs?label=npmx)](https://npmx.dev/package/@slux/rxjs) | [在线文档](https://itmanyong.github.io/slux/packages/rxjs) | Observable 与状态双向桥接 |
163
+
164
+ ### 中间件层
165
+
166
+ 挂载到 Store 的横切能力,五个包共用同一套契约。
167
+
168
+ | 包名 | 版本 | 在线文档 | 功能 |
169
+ | ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- | -------------------- |
170
+ | [`@slux/storage`](https://github.com/itmanyong/slux/tree/main/packages/storage) | [![npm version](https://img.shields.io/npm/v/@slux/storage)](https://www.npmjs.com/package/@slux/storage) [![npmx version](https://npmx.dev/api/registry/badge/version/@slux/storage?label=npmx)](https://npmx.dev/package/@slux/storage) | [在线文档](https://itmanyong.github.io/slux/packages/storage) | 状态持久化与恢复 |
171
+ | [`@slux/history`](https://github.com/itmanyong/slux/tree/main/packages/history) | [![npm version](https://img.shields.io/npm/v/@slux/history)](https://www.npmjs.com/package/@slux/history) [![npmx version](https://npmx.dev/api/registry/badge/version/@slux/history?label=npmx)](https://npmx.dev/package/@slux/history) | [在线文档](https://itmanyong.github.io/slux/packages/history) | 撤销与重做 |
172
+ | [`@slux/tab-sync`](https://github.com/itmanyong/slux/tree/main/packages/tab-sync) | [![npm version](https://img.shields.io/npm/v/@slux/tab-sync)](https://www.npmjs.com/package/@slux/tab-sync) [![npmx version](https://npmx.dev/api/registry/badge/version/@slux/tab-sync?label=npmx)](https://npmx.dev/package/@slux/tab-sync) | [在线文档](https://itmanyong.github.io/slux/packages/tab-sync) | 跨标签页状态同步 |
173
+ | [`@slux/telemetry`](https://github.com/itmanyong/slux/tree/main/packages/telemetry) | [![npm version](https://img.shields.io/npm/v/@slux/telemetry)](https://www.npmjs.com/package/@slux/telemetry) [![npmx version](https://npmx.dev/api/registry/badge/version/@slux/telemetry?label=npmx)](https://npmx.dev/package/@slux/telemetry) | [在线文档](https://itmanyong.github.io/slux/packages/telemetry) | 状态变化与订阅遥测 |
174
+ | [`@slux/devtools`](https://github.com/itmanyong/slux/tree/main/packages/devtools) | [![npm version](https://img.shields.io/npm/v/@slux/devtools)](https://www.npmjs.com/package/@slux/devtools) [![npmx version](https://npmx.dev/api/registry/badge/version/@slux/devtools?label=npmx)](https://npmx.dev/package/@slux/devtools) | [在线文档](https://itmanyong.github.io/slux/packages/devtools) | 调试工具、日志与检查 |
175
+
176
+ ### 框架层
177
+
178
+ 十二个绑定包都提供 core 主入口与六个范式子入口,使用框架原生响应式与生命周期。范式包是**可选** peer,只装实际使用的;`@slux/core` 与框架本身是**必需** peer。
179
+
180
+ | 包名 | 版本 | 在线文档 | 适配框架 | 适配版本 | 功能 |
181
+ | ------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | ----------------------------------------------- | ------------------------------------------ | ---------------------- |
182
+ | [`@slux/react`](https://github.com/itmanyong/slux/tree/main/packages/react) | [![npm version](https://img.shields.io/npm/v/@slux/react)](https://www.npmjs.com/package/@slux/react) [![npmx version](https://npmx.dev/api/registry/badge/version/@slux/react?label=npmx)](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 组件状态绑定 |
183
+ | [`@slux/vue`](https://github.com/itmanyong/slux/tree/main/packages/vue) | [![npm version](https://img.shields.io/npm/v/@slux/vue)](https://www.npmjs.com/package/@slux/vue) [![npmx version](https://npmx.dev/api/registry/badge/version/@slux/vue?label=npmx)](https://npmx.dev/package/@slux/vue) | [在线文档](https://itmanyong.github.io/slux/packages/vue) | [Vue](https://github.com/vuejs/core) | `>= 3.2.0` | Vue 组合式 API 绑定 |
184
+ | [`@slux/solid`](https://github.com/itmanyong/slux/tree/main/packages/solid) | [![npm version](https://img.shields.io/npm/v/@slux/solid)](https://www.npmjs.com/package/@slux/solid) [![npmx version](https://npmx.dev/api/registry/badge/version/@slux/solid?label=npmx)](https://npmx.dev/package/@slux/solid) | [在线文档](https://itmanyong.github.io/slux/packages/solid) | [Solid](https://github.com/solidjs/solid) | `>= 1.0.0` | Solid 细粒度响应式绑定 |
185
+ | [`@slux/svelte`](https://github.com/itmanyong/slux/tree/main/packages/svelte) | [![npm version](https://img.shields.io/npm/v/@slux/svelte)](https://www.npmjs.com/package/@slux/svelte) [![npmx version](https://npmx.dev/api/registry/badge/version/@slux/svelte?label=npmx)](https://npmx.dev/package/@slux/svelte) | [在线文档](https://itmanyong.github.io/slux/packages/svelte) | [Svelte](https://github.com/sveltejs/svelte) | `>= 4.0.0` | Svelte 原生 store 绑定 |
186
+ | [`@slux/angular`](https://github.com/itmanyong/slux/tree/main/packages/angular) | [![npm version](https://img.shields.io/npm/v/@slux/angular)](https://www.npmjs.com/package/@slux/angular) [![npmx version](https://npmx.dev/api/registry/badge/version/@slux/angular?label=npmx)](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 注入上下文绑定 |
187
+ | [`@slux/preact`](https://github.com/itmanyong/slux/tree/main/packages/preact) | [![npm version](https://img.shields.io/npm/v/@slux/preact)](https://www.npmjs.com/package/@slux/preact) [![npmx version](https://npmx.dev/api/registry/badge/version/@slux/preact?label=npmx)](https://npmx.dev/package/@slux/preact) | [在线文档](https://itmanyong.github.io/slux/packages/preact) | [Preact](https://github.com/preactjs/preact) | `>= 10.5.0` | Preact 组件状态绑定 |
188
+ | [`@slux/ripple`](https://github.com/itmanyong/slux/tree/main/packages/ripple) | [![npm version](https://img.shields.io/npm/v/@slux/ripple)](https://www.npmjs.com/package/@slux/ripple) [![npmx version](https://npmx.dev/api/registry/badge/version/@slux/ripple?label=npmx)](https://npmx.dev/package/@slux/ripple) | [在线文档](https://itmanyong.github.io/slux/packages/ripple) | [Ripple](https://github.com/Ripple-TS/ripple) | `>= 0.6.0` | Ripple 组件状态绑定 |
189
+ | [`@slux/inferno`](https://github.com/itmanyong/slux/tree/main/packages/inferno) | [![npm version](https://img.shields.io/npm/v/@slux/inferno)](https://www.npmjs.com/package/@slux/inferno) [![npmx version](https://npmx.dev/api/registry/badge/version/@slux/inferno?label=npmx)](https://npmx.dev/package/@slux/inferno) | [在线文档](https://itmanyong.github.io/slux/packages/inferno) | [Inferno](https://github.com/infernojs/inferno) | `>= 7.0.0` | Inferno 组件状态绑定 |
190
+ | [`@slux/octane`](https://github.com/itmanyong/slux/tree/main/packages/octane) | [![npm version](https://img.shields.io/npm/v/@slux/octane)](https://www.npmjs.com/package/@slux/octane) [![npmx version](https://npmx.dev/api/registry/badge/version/@slux/octane?label=npmx)](https://npmx.dev/package/@slux/octane) | [在线文档](https://itmanyong.github.io/slux/packages/octane) | [Octane](https://github.com/octanejs/octane) | `>= 0.16.0` | Octane 编译器绑定 |
191
+ | [`@slux/vitarx`](https://github.com/itmanyong/slux/tree/main/packages/vitarx) | [![npm version](https://img.shields.io/npm/v/@slux/vitarx)](https://www.npmjs.com/package/@slux/vitarx) [![npmx version](https://npmx.dev/api/registry/badge/version/@slux/vitarx?label=npmx)](https://npmx.dev/package/@slux/vitarx) | [在线文档](https://itmanyong.github.io/slux/packages/vitarx) | [Vitarx](https://github.com/vitarx-lib/core) | `>= 4.0.0` | Vitarx 组件状态绑定 |
192
+ | [`@slux/actview`](https://github.com/itmanyong/slux/tree/main/packages/actview) | [![npm version](https://img.shields.io/npm/v/@slux/actview)](https://www.npmjs.com/package/@slux/actview) [![npmx version](https://npmx.dev/api/registry/badge/version/@slux/actview?label=npmx)](https://npmx.dev/package/@slux/actview) | [在线文档](https://itmanyong.github.io/slux/packages/actview) | [ActView](https://github.com/actview/actview) | `>= 1.0.0` | ActView 组件状态绑定 |
193
+ | [`@slux/viewfly`](https://github.com/itmanyong/slux/tree/main/packages/viewfly) | [![npm version](https://img.shields.io/npm/v/@slux/viewfly)](https://www.npmjs.com/package/@slux/viewfly) [![npmx version](https://npmx.dev/api/registry/badge/version/@slux/viewfly?label=npmx)](https://npmx.dev/package/@slux/viewfly) | [在线文档](https://itmanyong.github.io/slux/packages/viewfly) | [Viewfly](https://github.com/viewfly/viewfly) | `>= 3.0.0` | Viewfly 组件状态绑定 |
194
+
195
+ 格式差异:**Octane** 的公开入口是 TypeScript 源码,需消费端 Octane 编译器处理且不提供 CJS;**Ripple** 只发布 ESM,另有 `./server` 服务端入口。其余边界见各包文档。
196
+
197
+ ### 插件层
198
+
199
+ **规划中**
200
+
201
+ | 包名 | 版本 | 在线文档 | 功能 |
202
+ | ---- | ---- | -------- | ---- |
203
+ | — | — | — | — |
204
+
205
+ <!-- package-navigation:end -->
package/dist/index.cjs ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";const __rslib_import_meta_url__="u"<typeof document?new(require("url".replace("",""))).URL("file:"+__filename).href:document.currentScript&&document.currentScript.src||new URL("main.js",document.baseURI).href;var __webpack_require__={};__webpack_require__.d=(e,t,r)=>{var a=(t,r)=>{for(var a in t)__webpack_require__.o(t,a)&&!__webpack_require__.o(e,a)&&Object.defineProperty(e,a,{enumerable:!0,[r]:t[a]})};a(t,"get"),a(r,"value")},__webpack_require__.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),__webpack_require__.r=e=>{"u">typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var __webpack_exports__={};function isBroadcastMessage(e){return"object"==typeof e&&null!==e&&"string"==typeof e.senderId&&"number"==typeof e.timestamp&&Number.isFinite(e.timestamp)&&"object"==typeof e.state&&null!==e.state&&!Array.isArray(e.state)}function generateSenderId(){return"u">typeof crypto&&"function"==typeof crypto.randomUUID?crypto.randomUUID():`fallback-${Math.random().toString(36).slice(2)}`}function broadcast(e){let t,{channel:r,filter:a,conflictResolution:o="last-write-wins"}=e,_=generateSenderId(),s=!1,p=0;return{name:"tab-sync",init(e){"u">typeof BroadcastChannel&&((t=new BroadcastChannel(r)).onmessage=t=>{let r=t.data;if(!isBroadcastMessage(r)||r.senderId===_)return;if("timestamp"===o){if(r.timestamp<=p)return;p=r.timestamp}let a="function"==typeof o?o(r.state,e.getState()):r.state;s=!0;try{e.setState(a)}finally{s=!1}})},onSet(e,r,o){if(r(o),s||void 0===t)return;let p={senderId:_,timestamp:Date.now(),state:void 0===a?o:a(e.getState())};t.postMessage(p)},onDestroy(){void 0!==t&&(t.onmessage=null,t.close(),t=void 0),p=0}}}__webpack_require__.r(__webpack_exports__),__webpack_require__.d(__webpack_exports__,{broadcast:()=>broadcast,createAutoImportApis:()=>createAutoImportApis});const shared_namespaceObject=require("@slux/shared"),allImportApis=["broadcast"],createAutoImportApis=(e="")=>(0,shared_namespaceObject.generateBatchAutoImports)([{prefix:e,fromName:"@slux/tab-sync",imports:allImportApis}]);for(var __rspack_i in exports.broadcast=__webpack_exports__.broadcast,exports.createAutoImportApis=__webpack_exports__.createAutoImportApis,__webpack_exports__)-1===["broadcast","createAutoImportApis"].indexOf(__rspack_i)&&(exports[__rspack_i]=__webpack_exports__[__rspack_i]);Object.defineProperty(exports,"__esModule",{value:!0});
2
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":["webpack://webpack/runtime/define_property_getters","webpack://webpack/runtime/has_own_property","webpack://webpack/runtime/make_namespace_object","../src/broadcast.ts","../src/auto-import-api.ts"],"sourcesContent":["__webpack_require__.d = (exports, getters, values) => {\n\tvar define = (defs, kind) => {\n\t\tfor(var key in defs) {\n\t\t\tif(__webpack_require__.o(defs, key) && !__webpack_require__.o(exports, key)) {\n\t\t\t\tObject.defineProperty(exports, key, { enumerable: true, [kind]: defs[key] });\n\t\t\t}\n\t\t}\n\t};\n\tdefine(getters, \"get\");\n\tdefine(values, \"value\");\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import type { Middleware } from \"@slux/store\";\nimport type { BroadcastMessage, BroadcastOptions } from \"./types.js\";\n\nfunction isBroadcastMessage<T>(data: unknown): data is BroadcastMessage<T> {\n if (typeof data !== \"object\" || data === null) return false;\n const message = data as Record<string, unknown>;\n return (\n typeof message[\"senderId\"] === \"string\" &&\n typeof message[\"timestamp\"] === \"number\" &&\n Number.isFinite(message[\"timestamp\"]) &&\n typeof message[\"state\"] === \"object\" &&\n message[\"state\"] !== null &&\n !Array.isArray(message[\"state\"])\n );\n}\n\nfunction generateSenderId(): string {\n if (typeof crypto !== \"undefined\" && typeof crypto.randomUUID === \"function\") return crypto.randomUUID();\n return `fallback-${Math.random().toString(36).slice(2)}`;\n}\n\nexport function broadcast<T extends object>(options: BroadcastOptions<T>): Middleware<T> {\n const { channel: channelName, filter, conflictResolution = \"last-write-wins\" } = options;\n const senderId = generateSenderId();\n let channel: BroadcastChannel | undefined;\n let applyingRemote = false;\n let lastRemoteTimestamp = 0;\n\n return {\n name: \"tab-sync\",\n init(api) {\n if (typeof BroadcastChannel === \"undefined\") return;\n channel = new BroadcastChannel(channelName);\n channel.onmessage = (event) => {\n const message: unknown = event.data;\n if (!isBroadcastMessage<T>(message) || message.senderId === senderId) return;\n if (conflictResolution === \"timestamp\") {\n if (message.timestamp <= lastRemoteTimestamp) return;\n lastRemoteTimestamp = message.timestamp;\n }\n const partial = typeof conflictResolution === \"function\" ? conflictResolution(message.state, api.getState()) : message.state;\n applyingRemote = true;\n try {\n api.setState(partial);\n } finally {\n // 业务订阅者抛错也不能让后续本地更新永久停止广播。\n applyingRemote = false;\n }\n };\n },\n onSet(api, next, partial) {\n next(partial);\n if (applyingRemote || channel === undefined) return;\n const message: BroadcastMessage<T> = {\n senderId,\n timestamp: Date.now(),\n state: filter === undefined ? partial : filter(api.getState()),\n };\n channel.postMessage(message);\n },\n onDestroy() {\n if (channel !== undefined) {\n channel.onmessage = null;\n channel.close();\n channel = undefined;\n }\n lastRemoteTimestamp = 0;\n },\n };\n}\n","import { generateBatchAutoImports } from \"@slux/shared\";\n\nconst allImportApis: string[] = [\"broadcast\"];\n\nexport const createAutoImportApis = (prefix: string = \"\") => {\n return generateBatchAutoImports([\n {\n prefix,\n fromName: \"@slux/tab-sync\",\n imports: allImportApis,\n },\n ]);\n};\n"],"names":["__webpack_require__","e","Object","Symbol","isBroadcastMessage","data","message","Number","Array","generateSenderId","crypto","Math","broadcast","options","channel","channelName","filter","conflictResolution","senderId","applyingRemote","lastRemoteTimestamp","api","BroadcastChannel","event","partial","next","undefined","Date","allImportApis","createAutoImportApis","prefix","generateBatchAutoImports"],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import type { Middleware } from '@slux/store';
2
+
3
+ export declare function broadcast<T extends object>(options: BroadcastOptions<T>): Middleware<T>;
4
+
5
+ export declare type BroadcastMessage<T> = {
6
+ readonly senderId: string;
7
+ readonly timestamp: number;
8
+ readonly state: Partial<T>;
9
+ };
10
+
11
+ export declare type BroadcastOptions<T> = {
12
+ readonly channel: string;
13
+ readonly filter?: (state: T) => Partial<T>;
14
+ /** timestamp 只与最近已接受的远端时间戳比较;本地更新不推进该值。 */
15
+ readonly conflictResolution?: ConflictResolution<T>;
16
+ };
17
+
18
+ export declare type ConflictResolution<T> = "last-write-wins" | "timestamp" | ConflictResolver<T>;
19
+
20
+ export declare type ConflictResolver<T> = (incoming: Partial<T>, current: T) => Partial<T>;
21
+
22
+ export declare const createAutoImportApis: (prefix?: string) => {
23
+ [x: string]: (string | [string, string])[];
24
+ };
25
+
26
+ export { }
@@ -0,0 +1,26 @@
1
+ import type { Middleware } from '@slux/store';
2
+
3
+ export declare function broadcast<T extends object>(options: BroadcastOptions<T>): Middleware<T>;
4
+
5
+ export declare type BroadcastMessage<T> = {
6
+ readonly senderId: string;
7
+ readonly timestamp: number;
8
+ readonly state: Partial<T>;
9
+ };
10
+
11
+ export declare type BroadcastOptions<T> = {
12
+ readonly channel: string;
13
+ readonly filter?: (state: T) => Partial<T>;
14
+ /** timestamp 只与最近已接受的远端时间戳比较;本地更新不推进该值。 */
15
+ readonly conflictResolution?: ConflictResolution<T>;
16
+ };
17
+
18
+ export declare type ConflictResolution<T> = "last-write-wins" | "timestamp" | ConflictResolver<T>;
19
+
20
+ export declare type ConflictResolver<T> = (incoming: Partial<T>, current: T) => Partial<T>;
21
+
22
+ export declare const createAutoImportApis: (prefix?: string) => {
23
+ [x: string]: (string | [string, string])[];
24
+ };
25
+
26
+ export { }
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ import{generateBatchAutoImports as t}from"@slux/shared";function e(t){let e,{channel:a,filter:o,conflictResolution:s="last-write-wins"}=t,n="u">typeof crypto&&"function"==typeof crypto.randomUUID?crypto.randomUUID():`fallback-${Math.random().toString(36).slice(2)}`,r=!1,i=0;return{name:"tab-sync",init(t){"u">typeof BroadcastChannel&&((e=new BroadcastChannel(a)).onmessage=e=>{let a=e.data;if(!("object"==typeof a&&null!==a&&"string"==typeof a.senderId&&"number"==typeof a.timestamp&&Number.isFinite(a.timestamp)&&"object"==typeof a.state&&null!==a.state&&!Array.isArray(a.state))||a.senderId===n)return;if("timestamp"===s){if(a.timestamp<=i)return;i=a.timestamp}let o="function"==typeof s?s(a.state,t.getState()):a.state;r=!0;try{t.setState(o)}finally{r=!1}})},onSet(t,a,s){if(a(s),r||void 0===e)return;let i={senderId:n,timestamp:Date.now(),state:void 0===o?s:o(t.getState())};e.postMessage(i)},onDestroy(){void 0!==e&&(e.onmessage=null,e.close(),e=void 0),i=0}}}let a=["broadcast"],o=(e="")=>t([{prefix:e,fromName:"@slux/tab-sync",imports:a}]);export{e as broadcast,o as createAutoImportApis};
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../src/broadcast.ts","../src/auto-import-api.ts"],"sourcesContent":["import type { Middleware } from \"@slux/store\";\nimport type { BroadcastMessage, BroadcastOptions } from \"./types.js\";\n\nfunction isBroadcastMessage<T>(data: unknown): data is BroadcastMessage<T> {\n if (typeof data !== \"object\" || data === null) return false;\n const message = data as Record<string, unknown>;\n return (\n typeof message[\"senderId\"] === \"string\" &&\n typeof message[\"timestamp\"] === \"number\" &&\n Number.isFinite(message[\"timestamp\"]) &&\n typeof message[\"state\"] === \"object\" &&\n message[\"state\"] !== null &&\n !Array.isArray(message[\"state\"])\n );\n}\n\nfunction generateSenderId(): string {\n if (typeof crypto !== \"undefined\" && typeof crypto.randomUUID === \"function\") return crypto.randomUUID();\n return `fallback-${Math.random().toString(36).slice(2)}`;\n}\n\nexport function broadcast<T extends object>(options: BroadcastOptions<T>): Middleware<T> {\n const { channel: channelName, filter, conflictResolution = \"last-write-wins\" } = options;\n const senderId = generateSenderId();\n let channel: BroadcastChannel | undefined;\n let applyingRemote = false;\n let lastRemoteTimestamp = 0;\n\n return {\n name: \"tab-sync\",\n init(api) {\n if (typeof BroadcastChannel === \"undefined\") return;\n channel = new BroadcastChannel(channelName);\n channel.onmessage = (event) => {\n const message: unknown = event.data;\n if (!isBroadcastMessage<T>(message) || message.senderId === senderId) return;\n if (conflictResolution === \"timestamp\") {\n if (message.timestamp <= lastRemoteTimestamp) return;\n lastRemoteTimestamp = message.timestamp;\n }\n const partial = typeof conflictResolution === \"function\" ? conflictResolution(message.state, api.getState()) : message.state;\n applyingRemote = true;\n try {\n api.setState(partial);\n } finally {\n // 业务订阅者抛错也不能让后续本地更新永久停止广播。\n applyingRemote = false;\n }\n };\n },\n onSet(api, next, partial) {\n next(partial);\n if (applyingRemote || channel === undefined) return;\n const message: BroadcastMessage<T> = {\n senderId,\n timestamp: Date.now(),\n state: filter === undefined ? partial : filter(api.getState()),\n };\n channel.postMessage(message);\n },\n onDestroy() {\n if (channel !== undefined) {\n channel.onmessage = null;\n channel.close();\n channel = undefined;\n }\n lastRemoteTimestamp = 0;\n },\n };\n}\n","import { generateBatchAutoImports } from \"@slux/shared\";\n\nconst allImportApis: string[] = [\"broadcast\"];\n\nexport const createAutoImportApis = (prefix: string = \"\") => {\n return generateBatchAutoImports([\n {\n prefix,\n fromName: \"@slux/tab-sync\",\n imports: allImportApis,\n },\n ]);\n};\n"],"names":["broadcast","options","channel","channelName","filter","conflictResolution","senderId","crypto","Math","applyingRemote","lastRemoteTimestamp","api","BroadcastChannel","event","message","data","Number","Array","partial","next","undefined","Date","allImportApis","createAutoImportApis","prefix","generateBatchAutoImports"],"mappings":"wDAqBO,SAASA,EAA4BC,CAA4B,EACtE,IAEIC,EAFE,CAAE,QAASC,CAAW,CAAEC,OAAAA,CAAM,CAAEC,mBAAAA,EAAqB,iBAAiB,CAAE,CAAGJ,EAC3EK,EANN,AAAI,AAAkB,IAAlB,OAAOC,QAA0B,AAA6B,YAA7B,OAAOA,OAAO,UAAU,CAAwBA,OAAO,UAAU,GAC/F,CAAC,SAAS,EAAEC,KAAK,MAAM,GAAG,QAAQ,CAAC,IAAI,KAAK,CAAC,GAAG,CAAC,CAOpDC,EAAiB,GACjBC,EAAsB,EAE1B,MAAO,CACL,KAAM,WACN,KAAKC,CAAG,EAC0B,IAA5B,OAAOC,kBAEXV,CADAA,CAAAA,EAAU,IAAIU,iBAAiBT,EAAW,EAClC,SAAS,CAAG,AAACU,IACnB,IAAMC,EAAmBD,EAAM,IAAI,CACnC,GAAI,CA/BV,CAAoB,UAAhB,OA+B6BC,GA/BDC,AAAS,OA+BRD,GA5B/B,AAA+B,UAA/B,OAAOA,AA4BwBA,EA5BhB,QAAW,EAC1B,AAAgC,UAAhC,OAAOA,AA2BwBA,EA3BhB,SAAY,EAC3BE,OAAO,QAAQ,CAACF,AA0BeA,EA1BP,SAAY,GACpC,AAA4B,UAA5B,OAAOA,AAyBwBA,EAzBhB,KAAQ,EACvBA,AAAqB,OAArBA,AAwB+BA,EAxBvB,KAAQ,EAChB,CAACG,MAAM,OAAO,CAACH,AAuBgBA,EAvBR,KAAQ,CAR0B,GA+BdA,EAAQ,QAAQ,GAAKR,EAAU,OACtE,GAAID,AAAuB,cAAvBA,EAAoC,CACtC,GAAIS,EAAQ,SAAS,EAAIJ,EAAqB,OAC9CA,EAAsBI,EAAQ,SAAS,AACzC,CACA,IAAMI,EAAU,AAA8B,YAA9B,OAAOb,EAAoCA,EAAmBS,EAAQ,KAAK,CAAEH,EAAI,QAAQ,IAAMG,EAAQ,KAAK,CAC5HL,EAAiB,GACjB,GAAI,CACFE,EAAI,QAAQ,CAACO,EACf,QAAU,CAERT,EAAiB,EACnB,CACF,EACF,EACA,MAAME,CAAG,CAAEQ,CAAI,CAAED,CAAO,EAEtB,GADAC,EAAKD,GACDT,GAAkBP,AAAYkB,SAAZlB,EAAuB,OAC7C,IAAMY,EAA+B,CACnCR,SAAAA,EACA,UAAWe,KAAK,GAAG,GACnB,MAAOjB,AAAWgB,SAAXhB,EAAuBc,EAAUd,EAAOO,EAAI,QAAQ,GAC7D,EACAT,EAAQ,WAAW,CAACY,EACtB,EACA,YACkBM,SAAZlB,IACFA,EAAQ,SAAS,CAAG,KACpBA,EAAQ,KAAK,GACbA,EAAUkB,QAEZV,EAAsB,CACxB,CACF,CACF,CCnEA,IAAMY,EAA0B,CAAC,YAAY,CAEhCC,EAAuB,CAACC,EAAiB,EAAE,GAC/CC,EAAyB,CAC9B,CACED,OAAAA,EACA,SAAU,iBACV,QAASF,CACX,EACD,S"}
package/package.json CHANGED
@@ -1,6 +1,69 @@
1
1
  {
2
2
  "name": "@slux/tab-sync",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.1.1",
4
+ "description": "基于 BroadcastChannel 的跨标签页状态同步",
5
+ "sideEffects": false,
6
+ "keywords": [
7
+ "slux",
8
+ "broadcast-channel",
9
+ "middleware",
10
+ "sync"
11
+ ],
12
+ "author": "itmanyong <itmanyong@gmail.com>",
13
+ "contributors": [
14
+ "itmanyong <itmanyong@gmail.com>"
15
+ ],
16
+ "publishConfig": {
17
+ "access": "public"
18
+ },
19
+ "license": "MIT",
20
+ "homepage": "https://www.npmjs.com/package/@slux/tab-sync",
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "git+https://github.com/itmanyong/slux.git",
24
+ "directory": "packages/tab-sync"
25
+ },
26
+ "bugs": {
27
+ "url": "https://github.com/itmanyong/slux/issues"
28
+ },
29
+ "type": "module",
30
+ "files": [
31
+ "dist",
32
+ "README.md",
33
+ "CHANGELOG.md",
34
+ "package.json"
35
+ ],
36
+ "main": "./dist/index.cjs",
37
+ "module": "./dist/index.js",
38
+ "types": "./dist/index.d.ts",
39
+ "exports": {
40
+ ".": {
41
+ "import": {
42
+ "types": "./dist/index.d.ts",
43
+ "default": "./dist/index.js"
44
+ },
45
+ "require": {
46
+ "types": "./dist/index.d.cts",
47
+ "default": "./dist/index.cjs"
48
+ }
49
+ },
50
+ "./package.json": "./package.json"
51
+ },
52
+ "dependencies": {
53
+ "@slux/store": "0.1.1",
54
+ "@slux/shared": "0.1.1"
55
+ },
56
+ "devDependencies": {
57
+ "rstack": "0.9.0",
58
+ "typescript": "7.1.0-beta"
59
+ },
60
+ "scripts": {
61
+ "dev": "rs lib -w",
62
+ "build": "rs lib",
63
+ "test": "rs test",
64
+ "lint": "rs lint",
65
+ "fmt": "rs fmt",
66
+ "typecheck": "tsc --noEmit",
67
+ "clean": "rm -rf dist node_modules .turbo"
68
+ }
6
69
  }