@free-walk/svelte-store 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 free-walk
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 ADDED
@@ -0,0 +1,185 @@
1
+ # svelte-store
2
+
3
+ Pinia 风格的 Svelte 5 状态管理库,基于 `svelte/store` 构建。
4
+
5
+ ## 特性
6
+
7
+ - 🏪 **Pinia 风格 API** — `defineStore` 定义 store,支持 Options API 和 Setup 风格
8
+ - 📦 **基于 svelte/store** — 完全兼容 Svelte 生态,支持 `$store` 自动订阅
9
+ - 🔌 **插件系统** — 支持全局插件,可扩展持久化、日志等功能
10
+ - 🎯 **TypeScript** — 完整类型推导
11
+ - ⚡ **轻量** — 零额外依赖,仅依赖 `svelte/store`
12
+
13
+ ## 安装
14
+
15
+ ```bash
16
+ npm install svelte-store
17
+ ```
18
+
19
+ ## 快速开始
20
+
21
+ ### Options API 风格
22
+
23
+ ```ts
24
+ import { defineStore } from 'svelte-store'
25
+
26
+ export const useCounterStore = defineStore('counter', {
27
+ state: () => ({
28
+ count: 0,
29
+ name: 'Counter',
30
+ }),
31
+ getters: {
32
+ doubleCount: (state) => state.count * 2,
33
+ displayName: (state) => `${state.name}: ${state.count}`,
34
+ },
35
+ actions: {
36
+ increment() {
37
+ this.$patch({ count: this.$state.count + 1 })
38
+ },
39
+ decrement() {
40
+ this.$patch({ count: this.$state.count - 1 })
41
+ },
42
+ incrementBy(amount: number) {
43
+ this.$patch({ count: this.$state.count + amount })
44
+ },
45
+ },
46
+ })
47
+ ```
48
+
49
+ ### Setup 风格
50
+
51
+ ```ts
52
+ import { defineStore, writable, derived } from 'svelte-store'
53
+
54
+ export const useCounterStore = defineStore('counter', () => {
55
+ const count = writable(0)
56
+ const doubleCount = derived(count, ($count) => $count * 2)
57
+
58
+ function increment() {
59
+ count.update((n) => n + 1)
60
+ }
61
+
62
+ function decrement() {
63
+ count.update((n) => n - 1)
64
+ }
65
+
66
+ return { count, doubleCount, increment, decrement }
67
+ })
68
+ ```
69
+
70
+ ### 在组件中使用
71
+
72
+ ```svelte
73
+ <script>
74
+ import { useCounterStore } from '../stores/counter'
75
+
76
+ const counter = useCounterStore()
77
+ </script>
78
+
79
+ <p>Count: {counter.$state.count}</p>
80
+ <p>Double: {counter.doubleCount}</p>
81
+
82
+ <button onclick={() => counter.increment()}>+</button>
83
+ <button onclick={() => counter.decrement()}>-</button>
84
+ ```
85
+
86
+ ## Store 实例 API
87
+
88
+ | 方法 / 属性 | 说明 |
89
+ |---|---|
90
+ | `$id` | Store 的唯一标识字符串 |
91
+ | `$state` | 获取当前 state 快照 |
92
+ | `$patch(partial)` | 批量更新 state(对象或函数) |
93
+ | `$reset()` | 重置 state 到初始值(仅 Options API) |
94
+ | `$subscribe(callback)` | 监听 state 变化,返回取消订阅函数 |
95
+ | `subscribe(run)` | svelte/store 标准订阅接口 |
96
+
97
+ ### $patch 用法
98
+
99
+ ```ts
100
+ // 对象方式
101
+ counter.$patch({ count: 10 })
102
+
103
+ // 函数方式
104
+ counter.$patch((state) => {
105
+ state.count++
106
+ state.name = 'Updated'
107
+ })
108
+ ```
109
+
110
+ ### $subscribe 用法
111
+
112
+ ```ts
113
+ const unsubscribe = counter.$subscribe((state) => {
114
+ console.log('state 变化:', state)
115
+ })
116
+
117
+ // 需要时取消订阅
118
+ unsubscribe()
119
+ ```
120
+
121
+ ## 插件系统
122
+
123
+ ```ts
124
+ import { addPlugin } from 'svelte-store'
125
+
126
+ // 持久化插件示例
127
+ addPlugin(({ store, storeId }) => {
128
+ // 恢复持久化数据
129
+ const key = `svelte-store-${storeId}`
130
+ const saved = localStorage.getItem(key)
131
+ if (saved) {
132
+ try {
133
+ store.$patch(JSON.parse(saved))
134
+ } catch {}
135
+ }
136
+
137
+ // 监听变化并持久化
138
+ store.$subscribe((state) => {
139
+ localStorage.setItem(key, JSON.stringify(state))
140
+ })
141
+ })
142
+
143
+ // 日志插件示例
144
+ addPlugin(({ store, storeId }) => {
145
+ store.$subscribe((state) => {
146
+ console.log(`[${storeId}]`, state)
147
+ })
148
+ })
149
+ ```
150
+
151
+ ## 辅助函数
152
+
153
+ ### mapState
154
+
155
+ 从 store 中提取状态为独立的 Readable store:
156
+
157
+ ```ts
158
+ import { mapState } from 'svelte-store'
159
+
160
+ const { count, name } = mapState(useCounterStore, ['count', 'name'])
161
+ // count 和 name 是 Readable<number> 和 Readable<string>
162
+ ```
163
+
164
+ ### mapActions
165
+
166
+ 从 store 中提取 actions 为独立函数:
167
+
168
+ ```ts
169
+ import { mapActions } from 'svelte-store'
170
+
171
+ const { increment, decrement } = mapActions(useCounterStore, ['increment', 'decrement'])
172
+ increment() // 直接调用
173
+ ```
174
+
175
+ ## 兼容性
176
+
177
+ | 格式 | 文件 | 用途 |
178
+ |---|---|---|
179
+ | ESM | `dist/svelte-store.mjs` | `import` 语法 |
180
+ | CJS | `dist/svelte-store.cjs` | `require()` 语法 |
181
+ | Svelte 源码 | `src/index.ts` | Svelte 项目直接引用 |
182
+
183
+ ## License
184
+
185
+ MIT
@@ -0,0 +1 @@
1
+ var l=Object.defineProperty;var R=Object.getOwnPropertyDescriptor;var v=Object.getOwnPropertyNames;var k=Object.prototype.hasOwnProperty;var j=(t,e)=>{for(var a in e)l(t,a,{get:e[a],enumerable:!0})},$=(t,e,a,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of v(e))!k.call(t,n)&&n!==a&&l(t,n,{get:()=>e[n],enumerable:!(r=R(e,n))||r.enumerable});return t};var O=t=>$(l({},"__esModule",{value:!0}),t);var W={};j(W,{addPlugin:()=>P,clearStores:()=>h,defineStore:()=>A,derived:()=>u.derived,get:()=>u.get,getRegisteredStore:()=>m,mapActions:()=>K,mapState:()=>D,readable:()=>u.readable,readonly:()=>u.readonly,writable:()=>u.writable});module.exports=O(W);var c=require("svelte/store"),u=require("svelte/store"),b=new Map,x=[];function A(t,e){return function(){if(b.has(t))return b.get(t);let r;typeof e=="function"?r=I(t,e):r=G(t,e),b.set(t,r);for(let n of x){let i=n({store:r,storeId:t,options:typeof e=="function"?{id:t}:e});i&&Object.assign(r,i)}return r}}function G(t,e){let a=e.state?e.state():{},r=(0,c.writable)({...a}),n={$id:t,subscribe:r.subscribe,get $state(){return(0,c.get)(r)},$patch(i){r.update(y=>typeof i=="function"?(i(y),{...y}):{...y,...i})},$reset(){let i=e.state?e.state():{};r.set({...i})},$subscribe(i){return r.subscribe(i)}};if(e.getters)for(let[i,y]of Object.entries(e.getters))Object.defineProperty(n,i,{get(){return y((0,c.get)(r))},enumerable:!0});if(e.actions)for(let[i,y]of Object.entries(e.actions))n[i]=(...d)=>y.apply(n,d);return n}function I(t,e){let a=e(),r=(0,c.writable)({}),n={},i={},y={};for(let[o,s]of Object.entries(a))typeof s=="function"?y[o]=s:s&&typeof s=="object"&&"subscribe"in s&&("set"in s?n[o]=s:i[o]=s);function d(){let o={};for(let[s,f]of Object.entries(n))o[s]=(0,c.get)(f);for(let[s,f]of Object.entries(i))o[s]=(0,c.get)(f);r.set(o)}let p=[];for(let o of Object.values(n))p.push(o.subscribe(()=>d()));for(let o of Object.values(i))p.push(o.subscribe(()=>d()));let g={$id:t,subscribe:r.subscribe,get $state(){return(0,c.get)(r)},$patch(o){if(typeof o=="function"){let s={};for(let[f,S]of Object.entries(n))s[f]=(0,c.get)(S);o(s);for(let[f,S]of Object.entries(n))f in s&&S.set(s[f])}else for(let[s,f]of Object.entries(o))s in n&&n[s].set(f)},$reset(){console.warn(`[svelte-store] Setup store "${t}" \u4E0D\u652F\u6301 $reset\uFF0C\u8BF7\u624B\u52A8\u91CD\u7F6E\u72B6\u6001`)},$subscribe(o){return r.subscribe(o)}};for(let[o,s]of Object.entries(y))g[o]=s;for(let[o,s]of Object.entries(n))Object.defineProperty(g,o,{get(){return(0,c.get)(s)},set(f){s.set(f)},enumerable:!0});for(let[o,s]of Object.entries(i))Object.defineProperty(g,o,{get(){return(0,c.get)(s)},enumerable:!0});return g}function P(t){x.push(t)}function m(t){return b.get(t)}function h(){b.clear()}function D(t,e){let a=t(),r={};for(let n of e)r[n]=(0,c.derived)({subscribe:a.subscribe},i=>i[n]);return r}function K(t,e){let a=t(),r={};for(let n of e)typeof a[n]=="function"&&(r[n]=a[n].bind(a));return r}
@@ -0,0 +1 @@
1
+ import{writable as l,derived as x,get as y}from"svelte/store";import{writable as D,readable as K,derived as W,get as F,readonly as w}from"svelte/store";var u=new Map,p=[];function $(o,i){return function(){if(u.has(o))return u.get(o);let t;typeof i=="function"?t=v(o,i):t=R(o,i),u.set(o,t);for(let r of p){let s=r({store:t,storeId:o,options:typeof i=="function"?{id:o}:i});s&&Object.assign(t,s)}return t}}function R(o,i){let c=i.state?i.state():{},t=l({...c}),r={$id:o,subscribe:t.subscribe,get $state(){return y(t)},$patch(s){t.update(f=>typeof s=="function"?(s(f),{...f}):{...f,...s})},$reset(){let s=i.state?i.state():{};t.set({...s})},$subscribe(s){return t.subscribe(s)}};if(i.getters)for(let[s,f]of Object.entries(i.getters))Object.defineProperty(r,s,{get(){return f(y(t))},enumerable:!0});if(i.actions)for(let[s,f]of Object.entries(i.actions))r[s]=(...b)=>f.apply(r,b);return r}function v(o,i){let c=i(),t=l({}),r={},s={},f={};for(let[n,e]of Object.entries(c))typeof e=="function"?f[n]=e:e&&typeof e=="object"&&"subscribe"in e&&("set"in e?r[n]=e:s[n]=e);function b(){let n={};for(let[e,a]of Object.entries(r))n[e]=y(a);for(let[e,a]of Object.entries(s))n[e]=y(a);t.set(n)}let S=[];for(let n of Object.values(r))S.push(n.subscribe(()=>b()));for(let n of Object.values(s))S.push(n.subscribe(()=>b()));let d={$id:o,subscribe:t.subscribe,get $state(){return y(t)},$patch(n){if(typeof n=="function"){let e={};for(let[a,g]of Object.entries(r))e[a]=y(g);n(e);for(let[a,g]of Object.entries(r))a in e&&g.set(e[a])}else for(let[e,a]of Object.entries(n))e in r&&r[e].set(a)},$reset(){console.warn(`[svelte-store] Setup store "${o}" \u4E0D\u652F\u6301 $reset\uFF0C\u8BF7\u624B\u52A8\u91CD\u7F6E\u72B6\u6001`)},$subscribe(n){return t.subscribe(n)}};for(let[n,e]of Object.entries(f))d[n]=e;for(let[n,e]of Object.entries(r))Object.defineProperty(d,n,{get(){return y(e)},set(a){e.set(a)},enumerable:!0});for(let[n,e]of Object.entries(s))Object.defineProperty(d,n,{get(){return y(e)},enumerable:!0});return d}function O(o){p.push(o)}function A(o){return u.get(o)}function G(){u.clear()}function I(o,i){let c=o(),t={};for(let r of i)t[r]=x({subscribe:c.subscribe},s=>s[r]);return t}function P(o,i){let c=o(),t={};for(let r of i)typeof c[r]=="function"&&(t[r]=c[r].bind(c));return t}export{O as addPlugin,G as clearStores,$ as defineStore,W as derived,F as get,A as getRegisteredStore,P as mapActions,I as mapState,K as readable,w as readonly,D as writable};
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@free-walk/svelte-store",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "description": "Pinia 风格的 Svelte 5 状态管理库,基于 svelte/store 构建",
6
+ "keywords": [
7
+ "svelte",
8
+ "svelte5",
9
+ "store",
10
+ "state-management",
11
+ "pinia"
12
+ ],
13
+ "license": "MIT",
14
+ "author": "free-walk",
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "https://github.com/webcodingnpc/svelte-store.git"
18
+ },
19
+ "svelte": "./src/index.ts",
20
+ "main": "./dist/svelte-store.cjs",
21
+ "module": "./dist/svelte-store.mjs",
22
+ "exports": {
23
+ ".": {
24
+ "svelte": "./src/index.ts",
25
+ "import": "./dist/svelte-store.mjs",
26
+ "require": "./dist/svelte-store.cjs",
27
+ "default": "./dist/svelte-store.mjs"
28
+ }
29
+ },
30
+ "files": [
31
+ "dist",
32
+ "src",
33
+ "README.md"
34
+ ],
35
+ "scripts": {
36
+ "build": "node scripts/build.js"
37
+ },
38
+ "peerDependencies": {
39
+ "svelte": "^5.0.0"
40
+ },
41
+ "devDependencies": {
42
+ "esbuild": "^0.27.7",
43
+ "svelte": "^5.55.1"
44
+ }
45
+ }
package/src/index.ts ADDED
@@ -0,0 +1,434 @@
1
+ /**
2
+ * svelte-store — Pinia 风格的 Svelte 5 状态管理库
3
+ *
4
+ * 核心概念:
5
+ * - defineStore:定义一个 store(类似 Pinia 的 defineStore)
6
+ * - state:响应式数据
7
+ * - getters:派生计算属性
8
+ * - actions:修改 state 的方法
9
+ * - plugins:全局插件系统
10
+ * - $subscribe:监听 state 变化
11
+ * - $patch:批量更新 state
12
+ * - $reset:重置 state 到初始值
13
+ */
14
+
15
+ import { writable, derived, get, readonly } from 'svelte/store'
16
+ import type { Writable, Readable, Unsubscriber } from 'svelte/store'
17
+
18
+ // ==================== 类型定义 ====================
19
+
20
+ /** Store 的 state 工厂函数 */
21
+ export type StateFactory<S> = () => S
22
+
23
+ /** Store 的 getters 定义 */
24
+ export type GettersDefinition<S, G> = {
25
+ [K in keyof G]: (state: S) => G[K]
26
+ }
27
+
28
+ /** Store 的 actions 定义 */
29
+ export type ActionsDefinition<S, A> = {
30
+ [K in keyof A]: A[K] extends (...args: infer P) => infer R
31
+ ? (this: StoreInstance<S, any, A>, ...args: P) => R
32
+ : never
33
+ }
34
+
35
+ /** Store Options API 风格定义 */
36
+ export interface StoreOptionsDefinition<
37
+ Id extends string,
38
+ S extends Record<string, any>,
39
+ G extends Record<string, any>,
40
+ A extends Record<string, (...args: any[]) => any>,
41
+ > {
42
+ id?: Id
43
+ state?: StateFactory<S>
44
+ getters?: GettersDefinition<S, G>
45
+ actions?: A
46
+ }
47
+
48
+ /** Setup 风格返回值 */
49
+ export type SetupReturn = Record<string, any>
50
+
51
+ /** Store 实例 */
52
+ export interface StoreInstance<
53
+ S extends Record<string, any>,
54
+ G extends Record<string, any>,
55
+ A extends Record<string, (...args: any[]) => any>,
56
+ > {
57
+ /** Store 唯一标识 */
58
+ $id: string
59
+ /** 订阅 state 变化 */
60
+ $subscribe: (callback: (state: S) => void) => Unsubscriber
61
+ /** 批量更新 state */
62
+ $patch: (partialOrUpdater: Partial<S> | ((state: S) => void)) => void
63
+ /** 重置 state 到初始值 */
64
+ $reset: () => void
65
+ /** 获取当前 state 快照 */
66
+ $state: S
67
+ /** svelte/store 订阅接口 */
68
+ subscribe: (run: (value: S) => void) => Unsubscriber
69
+ }
70
+
71
+ /** 插件上下文 */
72
+ export interface PluginContext<S = any> {
73
+ store: StoreInstance<S, any, any>
74
+ storeId: string
75
+ options: StoreOptionsDefinition<string, S, any, any>
76
+ }
77
+
78
+ /** 插件类型 */
79
+ export type StorePlugin = (context: PluginContext) => void | Record<string, any>
80
+
81
+ // ==================== 全局状态 ====================
82
+
83
+ /** 已注册的所有 store */
84
+ const storeRegistry = new Map<string, any>()
85
+
86
+ /** 已注册的插件 */
87
+ const plugins: StorePlugin[] = []
88
+
89
+ // ==================== 核心 API ====================
90
+
91
+ /**
92
+ * 定义一个 Store(Options API 风格)
93
+ *
94
+ * @example
95
+ * ```ts
96
+ * const useCounterStore = defineStore('counter', {
97
+ * state: () => ({ count: 0 }),
98
+ * getters: {
99
+ * double: (state) => state.count * 2,
100
+ * },
101
+ * actions: {
102
+ * increment() { this.$patch({ count: this.$state.count + 1 }) },
103
+ * decrement() { this.$patch({ count: this.$state.count - 1 }) },
104
+ * },
105
+ * })
106
+ *
107
+ * // 在组件中使用
108
+ * const counter = useCounterStore()
109
+ * $: console.log($counter) // { count: 0 }
110
+ * counter.increment()
111
+ * ```
112
+ */
113
+ export function defineStore<
114
+ Id extends string,
115
+ S extends Record<string, any> = {},
116
+ G extends Record<string, any> = {},
117
+ A extends Record<string, (...args: any[]) => any> = {},
118
+ >(
119
+ id: Id,
120
+ options: StoreOptionsDefinition<Id, S, G, A>,
121
+ ): () => StoreInstance<S, G, A> & S & { [K in keyof G]: G[K] } & A
122
+
123
+ /**
124
+ * 定义一个 Store(Setup 风格)
125
+ *
126
+ * @example
127
+ * ```ts
128
+ * const useCounterStore = defineStore('counter', () => {
129
+ * let count = writable(0)
130
+ * const double = derived(count, $c => $c * 2)
131
+ * function increment() { count.update(n => n + 1) }
132
+ * return { count, double, increment }
133
+ * })
134
+ * ```
135
+ */
136
+ export function defineStore<Id extends string>(
137
+ id: Id,
138
+ setup: () => SetupReturn,
139
+ ): () => any
140
+
141
+ export function defineStore(
142
+ id: string,
143
+ optionsOrSetup: StoreOptionsDefinition<string, any, any, any> | (() => SetupReturn),
144
+ ) {
145
+ return function useStore() {
146
+ // 单例:同一 id 只创建一次
147
+ if (storeRegistry.has(id)) {
148
+ return storeRegistry.get(id)
149
+ }
150
+
151
+ let store: any
152
+
153
+ if (typeof optionsOrSetup === 'function') {
154
+ // Setup 风格
155
+ store = createSetupStore(id, optionsOrSetup)
156
+ } else {
157
+ // Options API 风格
158
+ store = createOptionsStore(id, optionsOrSetup)
159
+ }
160
+
161
+ storeRegistry.set(id, store)
162
+
163
+ // 执行插件
164
+ for (const plugin of plugins) {
165
+ const extensions = plugin({
166
+ store,
167
+ storeId: id,
168
+ options: typeof optionsOrSetup === 'function' ? { id } : optionsOrSetup,
169
+ })
170
+ if (extensions) {
171
+ Object.assign(store, extensions)
172
+ }
173
+ }
174
+
175
+ return store
176
+ }
177
+ }
178
+
179
+ // ==================== Options Store 创建 ====================
180
+
181
+ function createOptionsStore(
182
+ id: string,
183
+ options: StoreOptionsDefinition<string, any, any, any>,
184
+ ) {
185
+ const initialState = options.state ? options.state() : {}
186
+ const stateStore: Writable<any> = writable({ ...initialState })
187
+
188
+ // 构建 store 实例
189
+ const store: any = {
190
+ $id: id,
191
+ subscribe: stateStore.subscribe,
192
+
193
+ get $state() {
194
+ return get(stateStore)
195
+ },
196
+
197
+ $patch(partialOrUpdater: any) {
198
+ stateStore.update((current: any) => {
199
+ if (typeof partialOrUpdater === 'function') {
200
+ partialOrUpdater(current)
201
+ return { ...current }
202
+ }
203
+ return { ...current, ...partialOrUpdater }
204
+ })
205
+ },
206
+
207
+ $reset() {
208
+ const freshState = options.state ? options.state() : {}
209
+ stateStore.set({ ...freshState })
210
+ },
211
+
212
+ $subscribe(callback: (state: any) => void) {
213
+ return stateStore.subscribe(callback)
214
+ },
215
+ }
216
+
217
+ // 绑定 getters
218
+ if (options.getters) {
219
+ for (const [key, getter] of Object.entries(options.getters)) {
220
+ Object.defineProperty(store, key, {
221
+ get() {
222
+ return (getter as Function)(get(stateStore))
223
+ },
224
+ enumerable: true,
225
+ })
226
+ }
227
+ }
228
+
229
+ // 绑定 actions(this 指向 store 实例)
230
+ if (options.actions) {
231
+ for (const [key, action] of Object.entries(options.actions)) {
232
+ store[key] = (...args: any[]) => (action as Function).apply(store, args)
233
+ }
234
+ }
235
+
236
+ return store
237
+ }
238
+
239
+ // ==================== Setup Store 创建 ====================
240
+
241
+ function createSetupStore(id: string, setup: () => SetupReturn) {
242
+ const result = setup()
243
+ const stateStore: Writable<any> = writable({})
244
+
245
+ // 分离 stores、computed 和 actions
246
+ const storeEntries: Record<string, Writable<any>> = {}
247
+ const readableEntries: Record<string, Readable<any>> = {}
248
+ const actionEntries: Record<string, Function> = {}
249
+
250
+ for (const [key, value] of Object.entries(result)) {
251
+ if (typeof value === 'function') {
252
+ actionEntries[key] = value
253
+ } else if (value && typeof value === 'object' && 'subscribe' in value) {
254
+ if ('set' in value) {
255
+ storeEntries[key] = value as Writable<any>
256
+ } else {
257
+ readableEntries[key] = value as Readable<any>
258
+ }
259
+ }
260
+ }
261
+
262
+ // 同步 state snapshot
263
+ function syncState() {
264
+ const state: any = {}
265
+ for (const [key, s] of Object.entries(storeEntries)) {
266
+ state[key] = get(s)
267
+ }
268
+ for (const [key, s] of Object.entries(readableEntries)) {
269
+ state[key] = get(s)
270
+ }
271
+ stateStore.set(state)
272
+ }
273
+
274
+ // 订阅所有 writable stores 的变化
275
+ const unsubs: Unsubscriber[] = []
276
+ for (const s of Object.values(storeEntries)) {
277
+ unsubs.push(s.subscribe(() => syncState()))
278
+ }
279
+ for (const s of Object.values(readableEntries)) {
280
+ unsubs.push(s.subscribe(() => syncState()))
281
+ }
282
+
283
+ const store: any = {
284
+ $id: id,
285
+ subscribe: stateStore.subscribe,
286
+
287
+ get $state() {
288
+ return get(stateStore)
289
+ },
290
+
291
+ $patch(partialOrUpdater: any) {
292
+ if (typeof partialOrUpdater === 'function') {
293
+ const current: any = {}
294
+ for (const [key, s] of Object.entries(storeEntries)) {
295
+ current[key] = get(s)
296
+ }
297
+ partialOrUpdater(current)
298
+ for (const [key, s] of Object.entries(storeEntries)) {
299
+ if (key in current) s.set(current[key])
300
+ }
301
+ } else {
302
+ for (const [key, value] of Object.entries(partialOrUpdater)) {
303
+ if (key in storeEntries) {
304
+ storeEntries[key].set(value)
305
+ }
306
+ }
307
+ }
308
+ },
309
+
310
+ $reset() {
311
+ // Setup store 没有初始工厂,不支持 $reset
312
+ console.warn(`[svelte-store] Setup store "${id}" 不支持 $reset,请手动重置状态`)
313
+ },
314
+
315
+ $subscribe(callback: (state: any) => void) {
316
+ return stateStore.subscribe(callback)
317
+ },
318
+ }
319
+
320
+ // 暴露 actions
321
+ for (const [key, action] of Object.entries(actionEntries)) {
322
+ store[key] = action
323
+ }
324
+
325
+ // 暴露 writable stores(通过 getter/setter 代理)
326
+ for (const [key, s] of Object.entries(storeEntries)) {
327
+ Object.defineProperty(store, key, {
328
+ get() { return get(s) },
329
+ set(value: any) { s.set(value) },
330
+ enumerable: true,
331
+ })
332
+ }
333
+
334
+ // 暴露 readable stores(通过 getter)
335
+ for (const [key, s] of Object.entries(readableEntries)) {
336
+ Object.defineProperty(store, key, {
337
+ get() { return get(s) },
338
+ enumerable: true,
339
+ })
340
+ }
341
+
342
+ return store
343
+ }
344
+
345
+ // ==================== 插件系统 ====================
346
+
347
+ /**
348
+ * 注册全局插件
349
+ *
350
+ * @example
351
+ * ```ts
352
+ * import { addPlugin } from 'svelte-store'
353
+ *
354
+ * // 持久化插件
355
+ * addPlugin(({ store, storeId }) => {
356
+ * const saved = localStorage.getItem(`store-${storeId}`)
357
+ * if (saved) store.$patch(JSON.parse(saved))
358
+ * store.$subscribe((state) => {
359
+ * localStorage.setItem(`store-${storeId}`, JSON.stringify(state))
360
+ * })
361
+ * })
362
+ * ```
363
+ */
364
+ export function addPlugin(plugin: StorePlugin): void {
365
+ plugins.push(plugin)
366
+ }
367
+
368
+ // ==================== 工具函数 ====================
369
+
370
+ /**
371
+ * 获取已注册的 store 实例(需先调用过 useStore)
372
+ */
373
+ export function getRegisteredStore(id: string): any | undefined {
374
+ return storeRegistry.get(id)
375
+ }
376
+
377
+ /**
378
+ * 清除所有已注册的 store(测试用)
379
+ */
380
+ export function clearStores(): void {
381
+ storeRegistry.clear()
382
+ }
383
+
384
+ /**
385
+ * 创建 store 映射辅助函数
386
+ * 类似 Pinia 的 mapState
387
+ *
388
+ * @example
389
+ * ```ts
390
+ * const useCounter = defineStore('counter', {
391
+ * state: () => ({ count: 0, name: 'Counter' }),
392
+ * })
393
+ *
394
+ * // mapState 提取部分 state
395
+ * const { count, name } = mapState(useCounter, ['count', 'name'])
396
+ * ```
397
+ */
398
+ export function mapState<S extends Record<string, any>>(
399
+ useStore: () => any,
400
+ keys: (keyof S)[],
401
+ ): Record<keyof S, Readable<any>> {
402
+ const store = useStore()
403
+ const result: any = {}
404
+ for (const key of keys) {
405
+ result[key] = derived({ subscribe: store.subscribe }, ($state: S) => $state[key])
406
+ }
407
+ return result
408
+ }
409
+
410
+ /**
411
+ * 转发 store 中的 actions
412
+ *
413
+ * @example
414
+ * ```ts
415
+ * const { increment, decrement } = mapActions(useCounter, ['increment', 'decrement'])
416
+ * ```
417
+ */
418
+ export function mapActions(
419
+ useStore: () => any,
420
+ keys: string[],
421
+ ): Record<string, (...args: any[]) => any> {
422
+ const store = useStore()
423
+ const result: any = {}
424
+ for (const key of keys) {
425
+ if (typeof store[key] === 'function') {
426
+ result[key] = store[key].bind(store)
427
+ }
428
+ }
429
+ return result
430
+ }
431
+
432
+ // 重新导出 svelte/store 常用 API
433
+ export { writable, readable, derived, get, readonly } from 'svelte/store'
434
+ export type { Writable, Readable, Unsubscriber } from 'svelte/store'