@composy/store-vue 0.0.1 → 0.0.2

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.
Files changed (251) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/README.md +169 -154
  3. package/dist/index.js +151 -53
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.min.js +242 -170
  6. package/dist/index.min.js.map +1 -1
  7. package/es/.dts-output-manifest.json +1 -0
  8. package/es/.dts.tsbuildinfo +1 -0
  9. package/es/composables/defineSetupStore.d.ts +0 -1
  10. package/es/composables/defineSetupStore.d.ts.map +1 -1
  11. package/es/composables/defineSetupStore.js +1 -1
  12. package/es/composables/defineSetupStore.js.map +1 -1
  13. package/es/composables/index.d.ts +8 -9
  14. package/es/composables/index.d.ts.map +1 -1
  15. package/es/composables/index.js +1 -1
  16. package/es/composables/useFormStore.d.ts +0 -1
  17. package/es/composables/useFormStore.d.ts.map +1 -1
  18. package/es/composables/useFormStore.js +1 -1
  19. package/es/composables/useFormStore.js.map +1 -1
  20. package/es/composables/useOptimistic.d.ts +1 -52
  21. package/es/composables/useOptimistic.d.ts.map +1 -1
  22. package/es/composables/useOptimistic.js +35 -20
  23. package/es/composables/useOptimistic.js.map +1 -1
  24. package/es/composables/usePersist.d.ts +0 -1
  25. package/es/composables/usePersist.d.ts.map +1 -1
  26. package/es/composables/usePersist.js +16 -7
  27. package/es/composables/usePersist.js.map +1 -1
  28. package/es/composables/usePersistedState.d.ts +0 -1
  29. package/es/composables/usePersistedState.d.ts.map +1 -1
  30. package/es/composables/usePersistedState.js +16 -3
  31. package/es/composables/usePersistedState.js.map +1 -1
  32. package/es/composables/useSharedState.d.ts +0 -1
  33. package/es/composables/useSharedState.d.ts.map +1 -1
  34. package/es/composables/useSharedState.js +25 -8
  35. package/es/composables/useSharedState.js.map +1 -1
  36. package/es/composables/useStore.d.ts +0 -1
  37. package/es/composables/useStore.js +1 -1
  38. package/es/composables/useStore.js.map +1 -1
  39. package/es/composables/useStoreDevtools.d.ts +0 -1
  40. package/es/composables/useStoreDevtools.d.ts.map +1 -1
  41. package/es/composables/useStoreDevtools.js +1 -1
  42. package/es/composables/useStoreDevtools.js.map +1 -1
  43. package/es/composables/useStoreSnapshot.d.ts +0 -1
  44. package/es/composables/useStoreSnapshot.d.ts.map +1 -1
  45. package/es/composables/useStoreSnapshot.js +18 -8
  46. package/es/composables/useStoreSnapshot.js.map +1 -1
  47. package/es/composables/useSubscribe.d.ts +0 -1
  48. package/es/composables/useSubscribe.d.ts.map +1 -1
  49. package/es/composables/useSubscribe.js +1 -1
  50. package/es/composables/useSubscribe.js.map +1 -1
  51. package/es/context/injection-keys.d.ts +0 -1
  52. package/es/context/injection-keys.d.ts.map +1 -1
  53. package/es/context/injection-keys.js +1 -1
  54. package/es/context/injection-keys.js.map +1 -1
  55. package/es/index.d.ts +1 -2
  56. package/es/index.js +2 -2
  57. package/es/index.js.map +1 -1
  58. package/es/plugins/engine-plugin.d.ts +1 -2
  59. package/es/plugins/engine-plugin.d.ts.map +1 -1
  60. package/es/plugins/engine-plugin.js +2 -2
  61. package/es/plugins/engine-plugin.js.map +1 -1
  62. package/es/plugins/enhanced-persist-plugin.d.ts +0 -1
  63. package/es/plugins/enhanced-persist-plugin.js +1 -1
  64. package/es/plugins/index.d.ts +0 -1
  65. package/es/plugins/index.js +1 -1
  66. package/es/plugins/pinia-plugin.d.ts +0 -1
  67. package/es/plugins/pinia-plugin.d.ts.map +1 -1
  68. package/es/plugins/pinia-plugin.js +24 -6
  69. package/es/plugins/pinia-plugin.js.map +1 -1
  70. package/es/plugins/registry-plugin.d.ts +0 -1
  71. package/es/plugins/registry-plugin.js +1 -1
  72. package/es/plugins/vue-plugin.d.ts +0 -1
  73. package/es/plugins/vue-plugin.d.ts.map +1 -1
  74. package/es/plugins/vue-plugin.js +1 -1
  75. package/es/plugins/vue-plugin.js.map +1 -1
  76. package/es/runtime/install-store-app.d.ts +0 -1
  77. package/es/runtime/install-store-app.js +1 -1
  78. package/es/runtime/install-store-app.js.map +1 -1
  79. package/es/runtime/store-plugin-options.d.ts +0 -1
  80. package/es/runtime/store-plugin-options.js +1 -1
  81. package/es/runtime/store-plugin-options.js.map +1 -1
  82. package/es/runtime/store-registry.d.ts +0 -1
  83. package/es/runtime/store-registry.d.ts.map +1 -1
  84. package/es/runtime/store-registry.js +14 -4
  85. package/es/runtime/store-registry.js.map +1 -1
  86. package/es/runtime/store-runtime.d.ts +1 -2
  87. package/es/runtime/store-runtime.d.ts.map +1 -1
  88. package/es/runtime/store-runtime.js +8 -2
  89. package/es/runtime/store-runtime.js.map +1 -1
  90. package/es/types.d.ts +2 -1
  91. package/es/types.d.ts.map +1 -1
  92. package/es/types.js +1 -1
  93. package/esm/composables/defineSetupStore.d.ts.map +1 -1
  94. package/esm/composables/defineSetupStore.js +1 -1
  95. package/esm/composables/defineSetupStore.js.map +1 -1
  96. package/esm/composables/index.d.ts +8 -8
  97. package/esm/composables/index.d.ts.map +1 -1
  98. package/esm/composables/index.js +1 -1
  99. package/esm/composables/useFormStore.d.ts.map +1 -1
  100. package/esm/composables/useFormStore.js +1 -1
  101. package/esm/composables/useFormStore.js.map +1 -1
  102. package/esm/composables/useOptimistic.d.ts +1 -51
  103. package/esm/composables/useOptimistic.d.ts.map +1 -1
  104. package/esm/composables/useOptimistic.js +35 -20
  105. package/esm/composables/useOptimistic.js.map +1 -1
  106. package/esm/composables/usePersist.d.ts.map +1 -1
  107. package/esm/composables/usePersist.js +16 -7
  108. package/esm/composables/usePersist.js.map +1 -1
  109. package/esm/composables/usePersistedState.d.ts.map +1 -1
  110. package/esm/composables/usePersistedState.js +16 -3
  111. package/esm/composables/usePersistedState.js.map +1 -1
  112. package/esm/composables/useSharedState.d.ts.map +1 -1
  113. package/esm/composables/useSharedState.js +25 -8
  114. package/esm/composables/useSharedState.js.map +1 -1
  115. package/esm/composables/useStore.js +1 -1
  116. package/esm/composables/useStore.js.map +1 -1
  117. package/esm/composables/useStoreDevtools.d.ts.map +1 -1
  118. package/esm/composables/useStoreDevtools.js +1 -1
  119. package/esm/composables/useStoreDevtools.js.map +1 -1
  120. package/esm/composables/useStoreSnapshot.d.ts.map +1 -1
  121. package/esm/composables/useStoreSnapshot.js +18 -8
  122. package/esm/composables/useStoreSnapshot.js.map +1 -1
  123. package/esm/composables/useSubscribe.d.ts.map +1 -1
  124. package/esm/composables/useSubscribe.js +1 -1
  125. package/esm/composables/useSubscribe.js.map +1 -1
  126. package/esm/context/injection-keys.d.ts.map +1 -1
  127. package/esm/context/injection-keys.js +1 -1
  128. package/esm/context/injection-keys.js.map +1 -1
  129. package/esm/index.d.ts +1 -1
  130. package/esm/index.js +2 -2
  131. package/esm/index.js.map +1 -1
  132. package/esm/plugins/engine-plugin.d.ts +1 -1
  133. package/esm/plugins/engine-plugin.d.ts.map +1 -1
  134. package/esm/plugins/engine-plugin.js +2 -2
  135. package/esm/plugins/engine-plugin.js.map +1 -1
  136. package/esm/plugins/enhanced-persist-plugin.js +1 -1
  137. package/esm/plugins/index.js +1 -1
  138. package/esm/plugins/pinia-plugin.d.ts.map +1 -1
  139. package/esm/plugins/pinia-plugin.js +24 -6
  140. package/esm/plugins/pinia-plugin.js.map +1 -1
  141. package/esm/plugins/registry-plugin.js +1 -1
  142. package/esm/plugins/vue-plugin.d.ts.map +1 -1
  143. package/esm/plugins/vue-plugin.js +1 -1
  144. package/esm/plugins/vue-plugin.js.map +1 -1
  145. package/esm/runtime/install-store-app.js +1 -1
  146. package/esm/runtime/install-store-app.js.map +1 -1
  147. package/esm/runtime/store-plugin-options.js +1 -1
  148. package/esm/runtime/store-plugin-options.js.map +1 -1
  149. package/esm/runtime/store-registry.d.ts.map +1 -1
  150. package/esm/runtime/store-registry.js +14 -4
  151. package/esm/runtime/store-registry.js.map +1 -1
  152. package/esm/runtime/store-runtime.d.ts +1 -1
  153. package/esm/runtime/store-runtime.d.ts.map +1 -1
  154. package/esm/runtime/store-runtime.js +8 -2
  155. package/esm/runtime/store-runtime.js.map +1 -1
  156. package/esm/types.d.ts +2 -0
  157. package/esm/types.d.ts.map +1 -1
  158. package/esm/types.js +1 -1
  159. package/lib/composables/defineSetupStore.cjs +1 -1
  160. package/lib/composables/defineSetupStore.cjs.map +1 -1
  161. package/lib/composables/{defineSetupStore.d.ts → defineSetupStore.d.cts} +0 -1
  162. package/lib/composables/defineSetupStore.d.cts.map +1 -0
  163. package/lib/composables/index.cjs +1 -1
  164. package/lib/composables/{index.d.ts → index.d.cts} +8 -9
  165. package/lib/composables/index.d.cts.map +1 -0
  166. package/lib/composables/useFormStore.cjs +1 -1
  167. package/lib/composables/useFormStore.cjs.map +1 -1
  168. package/lib/composables/{useFormStore.d.ts → useFormStore.d.cts} +0 -1
  169. package/lib/composables/useFormStore.d.cts.map +1 -0
  170. package/lib/composables/useOptimistic.cjs +35 -20
  171. package/lib/composables/useOptimistic.cjs.map +1 -1
  172. package/lib/composables/useOptimistic.d.cts +16 -0
  173. package/lib/composables/useOptimistic.d.cts.map +1 -0
  174. package/lib/composables/usePersist.cjs +16 -7
  175. package/lib/composables/usePersist.cjs.map +1 -1
  176. package/lib/composables/{usePersist.d.ts → usePersist.d.cts} +0 -1
  177. package/lib/composables/usePersist.d.cts.map +1 -0
  178. package/lib/composables/usePersistedState.cjs +16 -3
  179. package/lib/composables/usePersistedState.cjs.map +1 -1
  180. package/lib/composables/{usePersistedState.d.ts → usePersistedState.d.cts} +0 -1
  181. package/lib/composables/usePersistedState.d.cts.map +1 -0
  182. package/lib/composables/useSharedState.cjs +24 -7
  183. package/lib/composables/useSharedState.cjs.map +1 -1
  184. package/lib/composables/{useSharedState.d.ts → useSharedState.d.cts} +0 -1
  185. package/lib/composables/useSharedState.d.cts.map +1 -0
  186. package/lib/composables/useStore.cjs +1 -1
  187. package/lib/composables/useStore.cjs.map +1 -1
  188. package/lib/composables/{useStore.d.ts → useStore.d.cts} +0 -1
  189. package/lib/composables/useStore.d.cts.map +1 -0
  190. package/lib/composables/useStoreDevtools.cjs +1 -1
  191. package/lib/composables/useStoreDevtools.cjs.map +1 -1
  192. package/lib/composables/{useStoreDevtools.d.ts → useStoreDevtools.d.cts} +0 -1
  193. package/lib/composables/useStoreDevtools.d.cts.map +1 -0
  194. package/lib/composables/useStoreSnapshot.cjs +18 -8
  195. package/lib/composables/useStoreSnapshot.cjs.map +1 -1
  196. package/lib/composables/{useStoreSnapshot.d.ts → useStoreSnapshot.d.cts} +0 -1
  197. package/lib/composables/useStoreSnapshot.d.cts.map +1 -0
  198. package/lib/composables/useSubscribe.cjs +1 -1
  199. package/lib/composables/useSubscribe.cjs.map +1 -1
  200. package/lib/composables/{useSubscribe.d.ts → useSubscribe.d.cts} +0 -1
  201. package/lib/composables/useSubscribe.d.cts.map +1 -0
  202. package/lib/context/injection-keys.cjs +1 -1
  203. package/lib/context/injection-keys.cjs.map +1 -1
  204. package/lib/context/{injection-keys.d.ts → injection-keys.d.cts} +0 -1
  205. package/lib/context/injection-keys.d.cts.map +1 -0
  206. package/lib/index.cjs +2 -2
  207. package/lib/index.cjs.map +1 -1
  208. package/lib/{index.d.ts → index.d.cts} +1 -2
  209. package/lib/index.d.cts.map +1 -0
  210. package/lib/plugins/engine-plugin.cjs +2 -2
  211. package/lib/plugins/engine-plugin.cjs.map +1 -1
  212. package/lib/plugins/{engine-plugin.d.ts → engine-plugin.d.cts} +1 -2
  213. package/lib/plugins/engine-plugin.d.cts.map +1 -0
  214. package/lib/plugins/enhanced-persist-plugin.cjs +1 -1
  215. package/lib/plugins/{enhanced-persist-plugin.d.ts → enhanced-persist-plugin.d.cts} +0 -1
  216. package/lib/plugins/enhanced-persist-plugin.d.cts.map +1 -0
  217. package/lib/plugins/index.cjs +1 -1
  218. package/lib/plugins/{index.d.ts → index.d.cts} +0 -1
  219. package/lib/plugins/index.d.cts.map +1 -0
  220. package/lib/plugins/pinia-plugin.cjs +24 -6
  221. package/lib/plugins/pinia-plugin.cjs.map +1 -1
  222. package/lib/plugins/{pinia-plugin.d.ts → pinia-plugin.d.cts} +0 -1
  223. package/lib/plugins/pinia-plugin.d.cts.map +1 -0
  224. package/lib/plugins/registry-plugin.cjs +1 -1
  225. package/lib/plugins/{registry-plugin.d.ts → registry-plugin.d.cts} +0 -1
  226. package/lib/plugins/registry-plugin.d.cts.map +1 -0
  227. package/lib/plugins/vue-plugin.cjs +1 -1
  228. package/lib/plugins/vue-plugin.cjs.map +1 -1
  229. package/lib/plugins/{vue-plugin.d.ts → vue-plugin.d.cts} +0 -1
  230. package/lib/plugins/vue-plugin.d.cts.map +1 -0
  231. package/lib/runtime/install-store-app.cjs +1 -1
  232. package/lib/runtime/install-store-app.cjs.map +1 -1
  233. package/lib/runtime/{install-store-app.d.ts → install-store-app.d.cts} +0 -1
  234. package/lib/runtime/install-store-app.d.cts.map +1 -0
  235. package/lib/runtime/store-plugin-options.cjs +1 -1
  236. package/lib/runtime/store-plugin-options.cjs.map +1 -1
  237. package/lib/runtime/{store-plugin-options.d.ts → store-plugin-options.d.cts} +0 -1
  238. package/lib/runtime/store-plugin-options.d.cts.map +1 -0
  239. package/lib/runtime/store-registry.cjs +14 -4
  240. package/lib/runtime/store-registry.cjs.map +1 -1
  241. package/lib/runtime/{store-registry.d.ts → store-registry.d.cts} +0 -1
  242. package/lib/runtime/store-registry.d.cts.map +1 -0
  243. package/lib/runtime/store-runtime.cjs +8 -2
  244. package/lib/runtime/store-runtime.cjs.map +1 -1
  245. package/lib/runtime/{store-runtime.d.ts → store-runtime.d.cts} +1 -2
  246. package/lib/runtime/store-runtime.d.cts.map +1 -0
  247. package/lib/types.cjs +1 -1
  248. package/lib/{types.d.ts → types.d.cts} +2 -1
  249. package/lib/types.d.cts.map +1 -0
  250. package/package.json +19 -16
  251. package/lib/composables/useOptimistic.d.ts +0 -67
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/composables/defineSetupStore.ts","../src/composables/useFormStore.ts","../src/composables/useOptimistic.ts","../src/composables/usePersist.ts","../src/composables/usePersistedState.ts","../src/composables/useSharedState.ts","../src/composables/useStore.ts","../src/composables/useStoreDevtools.ts","../src/composables/useStoreSnapshot.ts","../src/composables/useSubscribe.ts","../src/context/injection-keys.ts","../src/plugins/pinia-plugin.ts","../src/runtime/store-plugin-options.ts","../src/runtime/store-registry.ts","../src/runtime/store-runtime.ts","../src/runtime/install-store-app.ts","../src/plugins/engine-plugin.ts","../src/plugins/vue-plugin.ts","../src/index.ts"],"sourcesContent":["/**\n * defineSetupStore - Setup 风格 Store 定义助手\n *\n * 提供更简洁的 composition API 风格来定义 Pinia Store。\n * 本质是对 defineStore 的 setup 语法的薄封装,增加类型推断和开发体验。\n *\n * @module composables/defineSetupStore\n */\n\nimport type {ComputedRef, Ref} from 'vue';\nimport { defineStore } from 'pinia'\nimport { computed, ref } from 'vue'\n\n/**\n * defineSetupStore - 使用 Composition API 风格定义 Store\n *\n * 与 defineStore 的 setup 语法等价,但提供更清晰的语义。\n * 适合喜欢 Composition API 风格的开发者。\n *\n * @param id - Store 唯一标识\n * @param setup - Setup 函数(在函数内使用 ref、computed、普通函数来定义 state、getters、actions)\n * @returns Pinia Store 定义\n *\n * @example\n * ```typescript\n * import { defineSetupStore } from '@composy/store-vue'\n * import { ref, computed } from 'vue'\n *\n * // Setup 风格定义(推荐)\n * export const useCounterStore = defineSetupStore('counter', () => {\n * // state(使用 ref)\n * const count = ref(0)\n * const step = ref(1)\n *\n * // getters(使用 computed)\n * const doubleCount = computed(() => count.value * 2)\n * const isPositive = computed(() => count.value > 0)\n *\n * // actions(普通函数)\n * function increment() {\n * count.value += step.value\n * }\n *\n * function decrement() {\n * count.value -= step.value\n * }\n *\n * async function fetchCount(id: string) {\n * const res = await fetch(`/api/count/${id}`)\n * count.value = await res.json()\n * }\n *\n * function reset() {\n * count.value = 0\n * step.value = 1\n * }\n *\n * return {\n * count, step,\n * doubleCount, isPositive,\n * increment, decrement, fetchCount, reset,\n * }\n * })\n * ```\n */\nexport function defineSetupStore<T>(\n id: string,\n setup: () => T,\n) {\n return defineStore(id, setup)\n}\n\n/**\n * createStoreState - 创建带有类型推断的 Store State\n *\n * 工具函数,帮助在 setup store 中快速创建多个 ref 状态。\n *\n * @param initial - 初始状态对象\n * @returns 以 ref 包装的响应式状态对象\n *\n * @example\n * ```typescript\n * const useUserStore = defineSetupStore('user', () => {\n * const state = createStoreState({\n * name: '张三',\n * age: 25,\n * email: '',\n * })\n *\n * // state.name 是 Ref<string>\n * // state.age 是 Ref<number>\n *\n * function updateName(name: string) {\n * state.name.value = name\n * }\n *\n * return { ...state, updateName }\n * })\n * ```\n */\nexport function createStoreState<T extends Record<string, unknown>>(\n initial: T,\n): { [K in keyof T]: Ref<T[K]> } {\n const result = {} as { [K in keyof T]: Ref<T[K]> }\n for (const key of Object.keys(initial) as Array<keyof T>) {\n result[key] = ref(initial[key]) as Ref<T[typeof key]>\n }\n return result\n}\n\n/**\n * createStoreGetters - 创建带有类型推断的 Store Getters\n *\n * 工具函数,帮助在 setup store 中快速创建多个 computed getter。\n *\n * @param getters - getter 函数映射\n * @returns 以 computed 包装的响应式 getter 对象\n *\n * @example\n * ```typescript\n * const useUserStore = defineSetupStore('user', () => {\n * const name = ref('张三')\n * const age = ref(25)\n *\n * const getters = createStoreGetters({\n * displayName: () => `${name.value} (${age.value}岁)`,\n * isAdult: () => age.value >= 18,\n * })\n *\n * return { name, age, ...getters }\n * })\n * ```\n */\nexport function createStoreGetters<T extends Record<string, () => unknown>>(\n getters: T,\n): { [K in keyof T]: ComputedRef<ReturnType<T[K]>> } {\n const result = {} as { [K in keyof T]: ComputedRef<ReturnType<T[K]>> }\n for (const key of Object.keys(getters) as Array<keyof T>) {\n result[key] = computed(getters[key]) as ComputedRef<ReturnType<T[typeof key]>>\n }\n return result\n}\n","/* ============================================================\n * useFormStore.ts\n * 表单状态管理 Composable\n * 提供表单值、字段状态、校验、提交、重置等完整表单生命周期管理\n * ============================================================ */\n\n/* 从 Vue 导入类型,用于声明响应式引用 */\nimport type { ComputedRef, Ref } from 'vue'\n/* 从 store-core 工具库导入深拷贝和深比较函数 */\nimport { deepClone, deepEqual } from '@composy/store-core'\n/* 从 Vue 导入响应式 API */\nimport { computed, reactive, ref, watch } from 'vue'\n\n/* ── 默认校验提示信息 ── */\n/* 必填项校验失败时的默认提示 */\nconst DEFAULT_REQUIRED_MESSAGE = '此字段为必填项'\n/* 正则匹配校验失败时的默认提示 */\nconst DEFAULT_PATTERN_MESSAGE = '格式不正确'\n/* 邮箱格式校验失败时的默认提示 */\nconst DEFAULT_EMAIL_MESSAGE = '邮箱格式不正确'\n/* 邮箱格式正则表达式 */\nconst EMAIL_PATTERN = /^[^\\s@]+@[^@][^.@]*\\.[^\\s@]+$/\n\n/* ── 类型定义 ── */\n\n/* 从泛型 T 中提取 string 类型的键名,用作字段标识 */\ntype FieldKey<T> = Extract<keyof T, string>\n\n/**\n * 表单校验器函数类型\n * @param value - 当前字段值\n * @param allValues - 表单所有字段的值(用于跨字段校验)\n * @returns 校验错误信息或 null(无错误),支持异步校验\n */\nexport type FormValidator<T = unknown> = (\n value: T,\n allValues: Record<string, unknown>,\n) => string | null | Promise<string | null>\n\n/**\n * 字段校验规则接口\n * @property required - 是否必填,可传 string 自定义提示\n * @property rules - 自定义校验器数组,按顺序依次执行\n */\nexport interface FormFieldRules<T = unknown> {\n required?: boolean | string\n rules?: FormValidator<T>[]\n}\n\n/**\n * useFormStore 的配置选项接口\n * @property initialValues - 表单初始值(必须提供完整对象)\n * @property validationRules - 各字段的校验规则映射\n * @property onSubmit - 表单提交回调(校验通过后调用)\n * @property validateOn - 触发校验的时机:'blur' | 'change' | 'submit'\n */\nexport interface UseFormStoreOptions<T extends Record<string, unknown>> {\n initialValues: T\n validationRules?: {\n [K in keyof T]?: FormFieldRules<T[K]>\n }\n onSubmit?: (values: T) => void | Promise<void>\n validateOn?: 'blur' | 'change' | 'submit'\n}\n\n/**\n * 单个字段的状态接口\n * @property touched - 字段是否被触碰过(获得过焦点或提交过)\n * @property dirty - 字段值是否与初始值不同(被修改过)\n * @property error - 当前校验错误信息,null 表示无错误\n * @property validating - 字段是否正在异步校验中\n */\nexport interface FormFieldState {\n touched: boolean\n dirty: boolean\n error: string | null\n validating: boolean\n}\n\n/**\n * useFormStore 返回值接口\n * @property values - 表单当前值(reactive 对象)\n * @property fields - 各字段的状态映射\n * @property isDirty - 是否有任意字段被修改过(计算属性)\n * @property isValid - 是否所有字段都无校验错误(计算属性)\n * @property isSubmitting - 表单是否正在提交中\n * @property submitError - 提交失败的错误信息\n * @property submitCount - 累计提交次数\n * @property setFieldValue - 设置指定字段的值\n * @property setFieldTouched - 标记字段为已触碰\n * @property validateField - 校验单个字段(异步)\n * @property validateAll - 校验所有字段(异步)\n * @property handleSubmit - 提交表单\n * @property reset - 重置整个表单(可传新的初始值)\n * @property resetField - 重置单个字段\n * @property errors - 所有字段错误信息的计算属性\n */\nexport interface UseFormStoreReturn<T extends Record<string, unknown>> {\n values: T\n fields: Record<string, FormFieldState>\n isDirty: ComputedRef<boolean>\n isValid: ComputedRef<boolean>\n isSubmitting: Ref<boolean>\n submitError: Ref<string | null>\n submitCount: Ref<number>\n setFieldValue: <K extends keyof T>(field: K, value: T[K]) => void\n setFieldTouched: (field: keyof T) => void\n validateField: (field: keyof T) => Promise<string | null>\n validateAll: () => Promise<boolean>\n handleSubmit: () => Promise<void>\n reset: (newInitialValues?: Partial<T>) => void\n resetField: (field: keyof T) => void\n errors: ComputedRef<Record<string, string | null>>\n}\n\n/**\n * 表单状态管理 Composable\n * 提供完整的表单生命周期管理能力,包括值管理、字段状态追踪、校验、提交和重置\n *\n * @param options - 表单配置选项\n * @returns 包含表单状态和操作方法的对象\n */\nexport function useFormStore<T extends Record<string, unknown>>(\n options: UseFormStoreOptions<T>,\n): UseFormStoreReturn<T> {\n /* 解构配置选项,设置默认值 */\n const {\n initialValues, /* 表单初始值 */\n validationRules = {} as NonNullable<UseFormStoreOptions<T>['validationRules']>, /* 校验规则,默认空对象 */\n onSubmit, /* 提交回调 */\n validateOn = 'blur', /* 校验时机,默认 blur 时触发 */\n } = options\n\n /* 深拷贝初始值并保存,用于后续重置和脏值比较 */\n const storedInitialValues = deepClone(initialValues) as T\n /* 创建响应式表单值对象(从初始值的深拷贝开始) */\n const values = reactive(deepClone(storedInitialValues)) as T\n /* 将 values 转为通用 Record 类型,便于动态属性访问 */\n const valueRecord = values as Record<string, unknown>\n /* 将初始值也转为 Record 类型,用于脏值比较 */\n const initialValueRecord = storedInitialValues as Record<string, unknown>\n /* 获取所有字段名列表 */\n const fieldKeys = Object.keys(storedInitialValues) as Array<FieldKey<T>>\n\n /* 创建每个字段的响应式状态对象(touched/dirty/error/validating) */\n const fields = reactive(\n fieldKeys.reduce<Record<string, FormFieldState>>((acc, key) => {\n acc[key] = {\n touched: false, /* 字段未被触碰 */\n dirty: false, /* 字段值未被修改 */\n error: null, /* 无校验错误 */\n validating: false, /* 未在校验中 */\n }\n return acc\n }, {}),\n )\n\n /* ── 全局状态 ── */\n /* 是否正在提交中 */\n const isSubmitting = ref(false)\n /* 提交错误信息 */\n const submitError = ref<string | null>(null)\n /* 累计提交次数 */\n const submitCount = ref(0)\n\n /* ── 计算属性 ── */\n /* 是否有任意字段被修改过 */\n const isDirty = computed(() => fieldKeys.some(key => fields[key]?.dirty))\n /* 是否所有字段都无错误 */\n const isValid = computed(() => fieldKeys.every(key => !fields[key]?.error))\n\n /* 所有字段错误信息的映射 */\n const errors = computed(() => {\n return fieldKeys.reduce<Record<string, string | null>>((acc, key) => {\n acc[key] = fields[key]?.error ?? null\n return acc\n }, {})\n })\n\n /* ── 值变化监听 ── */\n /* 为每个字段创建 watch,当值变化时更新脏状态并按需触发校验 */\n fieldKeys.forEach((key) => {\n watch(\n () => valueRecord[key], /* 监听字段值 */\n (newValue) => {\n const field = fields[key]\n if (field) {\n /* 比较当前值与初始值,判断是否为脏值 */\n field.dirty = !deepEqual(newValue, initialValueRecord[key])\n }\n\n /* 如果校验时机为 'change' 且字段已被触碰,则触发校验 */\n if (validateOn === 'change' && field?.touched) {\n void validateField(key)\n }\n },\n { deep: true }, /* 深度监听,支持嵌套对象变化 */\n )\n })\n\n /* ── 操作方法 ── */\n\n /**\n * 设置指定字段的值\n * @param field - 字段名\n * @param value - 新值\n */\n function setFieldValue<K extends keyof T>(field: K, value: T[K]): void {\n valueRecord[field as string] = value\n }\n\n /**\n * 标记字段为已触碰状态\n * 如果校验时机为 'blur',同时触发该字段的校验\n * @param field - 字段名\n */\n function setFieldTouched(field: keyof T): void {\n const fieldState = fields[field as string]\n if (fieldState) {\n fieldState.touched = true\n }\n\n /* blur 时自动触发校验 */\n if (validateOn === 'blur') {\n void validateField(field)\n }\n }\n\n /**\n * 校验单个字段\n * 按顺序执行:必填校验 → 自定义规则校验\n * @param field - 字段名\n * @returns 错误信息或 null(无错误)\n */\n async function validateField(field: keyof T): Promise<string | null> {\n const fieldKey = field as string\n const fieldState = fields[fieldKey]\n\n /* 字段状态不存在则跳过 */\n if (!fieldState) {\n return null\n }\n\n /* 获取该字段的校验规则 */\n const fieldRules = validationRules[field]\n /* 无规则则清除错误并返回 */\n if (!fieldRules) {\n fieldState.error = null\n return null\n }\n\n /* 标记开始校验 */\n fieldState.validating = true\n\n try {\n const value = values[field]\n\n /* 第一步:必填校验 */\n if (fieldRules.required) {\n const isEmpty = value === '' || value === null || value === undefined\n if (isEmpty) {\n /* 支持自定义必填提示信息 */\n const message = typeof fieldRules.required === 'string'\n ? fieldRules.required\n : DEFAULT_REQUIRED_MESSAGE\n fieldState.error = message\n return message\n }\n }\n\n /* 第二步:自定义规则校验(按顺序执行,遇到第一个错误即停止) */\n if (fieldRules.rules) {\n for (const rule of fieldRules.rules) {\n const error = await rule(\n value as T[keyof T],\n valueRecord, /* 传入所有表单值,支持跨字段校验 */\n )\n\n if (error) {\n fieldState.error = error\n return error\n }\n }\n }\n\n /* 所有校验通过,清除错误 */\n fieldState.error = null\n return null\n }\n finally {\n /* 无论成功失败,标记校验结束 */\n fieldState.validating = false\n }\n }\n\n /**\n * 校验所有字段\n * @returns 是否所有字段都校验通过\n */\n async function validateAll(): Promise<boolean> {\n const results = await Promise.all(fieldKeys.map(key => validateField(key)))\n return results.every(result => result === null)\n }\n\n /**\n * 提交流程\n * 1. 递增提交计数\n * 2. 标记所有字段为已触碰\n * 3. 执行全量校验\n * 4. 校验通过且有 onSubmit 回调时,调用提交\n */\n async function handleSubmit(): Promise<void> {\n submitCount.value++\n submitError.value = null\n\n /* 标记所有字段为已触碰 */\n fieldKeys.forEach((key) => {\n const field = fields[key]\n if (field) {\n field.touched = true\n }\n })\n\n /* 执行全量校验 */\n const valid = await validateAll()\n /* 校验未通过或没有提交回调则退出 */\n if (!valid || !onSubmit) {\n return\n }\n\n /* 开始提交 */\n isSubmitting.value = true\n try {\n /* 深拷贝 values 后传给 onSubmit,避免外部修改响应式数据 */\n await onSubmit(deepClone(values) as T)\n }\n catch (error) {\n /* 捕获提交异常,保存错误信息 */\n submitError.value = error instanceof Error ? error.message : String(error)\n }\n finally {\n /* 无论成功失败,标记提交结束 */\n isSubmitting.value = false\n }\n }\n\n /**\n * 重置整个表单\n * 可选传入新的初始值(部分更新)\n * @param newInitialValues - 新的初始值(可选,会与现有初始值合并)\n */\n function reset(newInitialValues?: Partial<T>): void {\n /* 如果传入了新初始值,合并到存储的初始值中 */\n if (newInitialValues) {\n Object.assign(initialValueRecord, deepClone(newInitialValues))\n }\n\n /* 恢复每个字段的值和状态 */\n fieldKeys.forEach((key) => {\n /* 恢复为初始值的深拷贝 */\n valueRecord[key] = deepClone(initialValueRecord[key])\n\n /* 重置字段状态 */\n const field = fields[key]\n if (field) {\n field.touched = false /* 清除触碰状态 */\n field.dirty = false /* 清除脏值标记 */\n field.error = null /* 清除校验错误 */\n field.validating = false /* 清除校验中标记 */\n }\n })\n\n /* 清除提交错误 */\n submitError.value = null\n }\n\n /**\n * 重置单个字段\n * @param field - 要重置的字段名\n */\n function resetField(field: keyof T): void {\n const fieldKey = field as string\n /* 恢复字段值为初始值的深拷贝 */\n valueRecord[fieldKey] = deepClone(initialValueRecord[fieldKey])\n\n /* 重置字段状态 */\n const fieldState = fields[fieldKey]\n if (fieldState) {\n fieldState.touched = false\n fieldState.dirty = false\n fieldState.error = null\n fieldState.validating = false\n }\n }\n\n /* ── 返回所有状态和方法 ── */\n return {\n values, /* 表单当前值(响应式) */\n fields, /* 字段状态映射(响应式) */\n isDirty, /* 是否有脏值 */\n isValid, /* 是否全部有效 */\n isSubmitting, /* 是否正在提交 */\n submitError, /* 提交错误信息 */\n submitCount, /* 提交次数 */\n setFieldValue, /* 设置字段值 */\n setFieldTouched, /* 标记字段已触碰 */\n validateField, /* 校验单个字段 */\n validateAll, /* 校验所有字段 */\n handleSubmit, /* 提交表单 */\n reset, /* 重置表单 */\n resetField, /* 重置单个字段 */\n errors, /* 错误信息映射 */\n }\n}\n\n/* ============================================================\n * 预置校验器工厂函数\n * 提供常用的表单校验规则创建函数\n * ============================================================ */\n\n/**\n * 创建必填校验器\n * @param message - 自定义错误提示,默认为 '此字段为必填项'\n * @returns 校验器函数\n */\nexport function required(message = DEFAULT_REQUIRED_MESSAGE): FormValidator {\n return (value) => {\n /* 空字符串、null、undefined 均视为\"空\" */\n if (value === '' || value === null || value === undefined) {\n return message\n }\n\n return null /* 通过校验 */\n }\n}\n\n/**\n * 创建最小长度校验器\n * @param min - 最小字符数\n * @param message - 自定义错误提示\n * @returns 字符串校验器函数\n */\nexport function minLength(min: number, message?: string): FormValidator<string> {\n return (value) => {\n if (typeof value === 'string' && value.length < min) {\n return message ?? `至少需要 ${min} 个字符`\n }\n\n return null\n }\n}\n\n/**\n * 创建最大长度校验器\n * @param max - 最大字符数\n * @param message - 自定义错误提示\n * @returns 字符串校验器函数\n */\nexport function maxLength(max: number, message?: string): FormValidator<string> {\n return (value) => {\n if (typeof value === 'string' && value.length > max) {\n return message ?? `不能超过 ${max} 个字符`\n }\n\n return null\n }\n}\n\n/**\n * 创建正则匹配校验器\n * @param regex - 正则表达式\n * @param message - 自定义错误提示,默认为 '格式不正确'\n * @returns 字符串校验器函数\n */\nexport function pattern(\n regex: RegExp,\n message = DEFAULT_PATTERN_MESSAGE,\n): FormValidator<string> {\n return (value) => {\n if (typeof value === 'string' && !regex.test(value)) {\n return message\n }\n\n return null\n }\n}\n\n/**\n * 创建邮箱格式校验器\n * 基于内置的 EMAIL_PATTERN 正则\n * @param message - 自定义错误提示,默认为 '邮箱格式不正确'\n * @returns 字符串校验器函数\n */\nexport function email(message = DEFAULT_EMAIL_MESSAGE): FormValidator<string> {\n return pattern(EMAIL_PATTERN, message)\n}\n\n/**\n * 创建数值范围校验器\n * @param min - 最小值\n * @param max - 最大值\n * @param message - 自定义错误提示\n * @returns 数字校验器函数\n */\nexport function numberRange(\n min: number,\n max: number,\n message?: string,\n): FormValidator<number> {\n return (value) => {\n if (typeof value === 'number' && (value < min || value > max)) {\n return message ?? `必须在 ${min} 到 ${max} 之间`\n }\n\n return null\n }\n}\n","/**\n * useOptimistic - 乐观更新 Hook\n *\n * 提供乐观更新模式:先立即更新 UI,再异步确认,失败时自动回滚\n * @module composables/useOptimistic\n */\n\nimport type {Ref} from 'vue';\nimport { deepClone } from '@composy/store-core'\nimport { ref } from 'vue'\n\n/**\n * 乐观更新操作记录\n */\nexport interface OptimisticEntry<T> {\n /** 操作 ID */\n id: string\n /** 提交前的快照 */\n snapshot: T\n /** 操作状态 */\n status: 'pending' | 'confirmed' | 'rolled-back'\n /** 时间戳 */\n timestamp: number\n}\n\n/**\n * useOptimistic 返回值\n */\nexport interface UseOptimisticReturn<T> {\n /** 响应式状态 */\n state: Ref<T>\n /** 是否有挂起的操作 */\n isPending: Ref<boolean>\n /** 挂起的操作记录 */\n pending: Ref<OptimisticEntry<T>[]>\n /** 最近一次操作是否已回滚 */\n isRolledBack: Ref<boolean>\n /** 执行乐观更新: 先 apply 新值,然后 confirm 异步确认 */\n mutate: (\n optimisticValue: T | ((prev: T) => T),\n confirm: () => Promise<void>,\n ) => Promise<void>\n}\n\n/**\n * 生成唯一 ID\n */\nfunction uid(): string {\n return `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`\n}\n\n/**\n * 乐观更新 Hook\n *\n * 先立即更新 UI(乐观),再执行异步确认操作。\n * 若确认失败则自动回滚到更新前的状态。\n *\n * @template T - 状态类型\n * @param initialState - 初始状态\n * @returns 乐观更新 API\n *\n * @example\n * ```typescript\n * const { state, isPending, isRolledBack, mutate } = useOptimistic({\n * items: ['Apple', 'Banana'],\n * })\n *\n * // 乐观添加\n * await mutate(\n * (prev) => ({ ...prev, items: [...prev.items, 'Cherry'] }),\n * async () => {\n * await fetch('/api/items', {\n * method: 'POST',\n * body: JSON.stringify({ name: 'Cherry' }),\n * })\n * }\n * )\n * // 成功: state 保持新值\n * // 失败: state 自动回滚到添加之前\n * ```\n */\nexport function useOptimistic<T>(initialState: T): UseOptimisticReturn<T> {\n const state = ref<T>(deepClone(initialState)) as Ref<T>\n const isPending = ref(false)\n const isRolledBack = ref(false)\n const pending = ref<OptimisticEntry<T>[]>([]) as Ref<OptimisticEntry<T>[]>\n\n async function mutate(\n optimisticValue: T | ((prev: T) => T),\n confirm: () => Promise<void>,\n ): Promise<void> {\n // 保存快照\n const snapshot: T = deepClone(state.value)\n const entryId = uid()\n const entry: OptimisticEntry<T> = {\n id: entryId,\n snapshot,\n status: 'pending',\n timestamp: Date.now(),\n }\n\n pending.value = [...pending.value, entry]\n isPending.value = true\n isRolledBack.value = false\n\n // 立即应用乐观值\n if (typeof optimisticValue === 'function') {\n state.value = (optimisticValue as (prev: T) => T)(state.value)\n }\n else {\n state.value = optimisticValue\n }\n\n try {\n // 异步确认\n await confirm()\n\n // 确认成功\n pending.value = pending.value.map(e =>\n e.id === entryId ? { ...e, status: 'confirmed' as const } : e,\n )\n }\n catch {\n // 确认失败 - 回滚\n state.value = snapshot\n isRolledBack.value = true\n\n pending.value = pending.value.map(e =>\n e.id === entryId ? { ...e, status: 'rolled-back' as const } : e,\n )\n }\n finally {\n // 清除已完成的条目\n pending.value = pending.value.filter(e => e.status === 'pending')\n isPending.value = pending.value.length > 0\n }\n }\n\n return {\n state,\n isPending,\n pending,\n isRolledBack,\n mutate,\n }\n}\n","/**\n * usePersist Composable\n *\n * 提供状态持久化功能\n * @module composables/usePersist\n */\n\nimport type { Ref } from 'vue'\nimport type { UsePersistOptions, UsePersistReturn } from '../types'\nimport { onMounted, ref, watch } from 'vue'\n\n/**\n * 获取存储对象\n */\nfunction getStorage(type: 'localStorage' | 'sessionStorage'): Storage | null {\n if (typeof window === 'undefined') {\n return null\n }\n return type === 'localStorage' ? localStorage : sessionStorage\n}\n\n/**\n * 状态持久化 Hook\n *\n * 将状态自动保存到 localStorage 或 sessionStorage\n *\n * @template T - 值类型\n * @param options - 持久化选项\n * @returns 持久化操作对象\n *\n * @example\n * ```typescript\n * const { value, save, restore, clear } = usePersist<User>({\n * key: 'user-info',\n * storage: 'localStorage',\n * defaultValue: { name: '', age: 0 }\n * })\n *\n * // 更新值会自动保存\n * value.value = { name: 'John', age: 25 }\n *\n * // 手动保存\n * save()\n *\n * // 从存储恢复\n * restore()\n *\n * // 清除存储\n * clear()\n * ```\n */\nexport function usePersist<T>(options: UsePersistOptions): UsePersistReturn<T> {\n const {\n key,\n storage: storageType = 'localStorage',\n defaultValue,\n serialize = JSON.stringify,\n deserialize = JSON.parse,\n } = options\n\n const storage = getStorage(storageType)\n const value = ref<T>(defaultValue as T) as Ref<T>\n\n /**\n * 从存储恢复\n */\n const restore = (): void => {\n if (!storage) return\n\n try {\n const stored = storage.getItem(key)\n if (stored !== null) {\n value.value = deserialize(stored) as T\n }\n }\n catch (error) {\n console.error(`[usePersist] 恢复失败 (${key}):`, error)\n }\n }\n\n /**\n * 保存到存储\n */\n const save = (): void => {\n if (!storage) return\n\n try {\n storage.setItem(key, serialize(value.value))\n }\n catch (error) {\n console.error(`[usePersist] 保存失败 (${key}):`, error)\n }\n }\n\n /**\n * 清除存储\n */\n const clear = (): void => {\n if (!storage) return\n\n try {\n storage.removeItem(key)\n value.value = defaultValue as T\n }\n catch (error) {\n console.error(`[usePersist] 清除失败 (${key}):`, error)\n }\n }\n\n // 组件挂载时恢复\n onMounted(() => {\n restore()\n })\n\n // 监听变化自动保存\n watch(\n value,\n () => {\n save()\n },\n { deep: true },\n )\n\n return {\n value,\n save,\n restore,\n clear,\n }\n}\n\n/**\n * 创建持久化的 ref\n *\n * @template T - 值类型\n * @param key - 存储键名\n * @param defaultValue - 默认值\n * @param storage - 存储类型\n * @returns 持久化的 ref\n *\n * @example\n * ```typescript\n * const count = usePersistedRef('count', 0)\n *\n * // 更新会自动保存\n * count.value++\n * ```\n */\nexport function usePersistedRef<T>(\n key: string,\n defaultValue: T,\n storage: 'localStorage' | 'sessionStorage' = 'localStorage',\n): Ref<T> {\n const { value } = usePersist<T>({\n key,\n storage,\n defaultValue,\n })\n return value\n}\n\n/**\n * 创建带过期时间的持久化 ref\n *\n * @template T - 值类型\n * @param key - 存储键名\n * @param defaultValue - 默认值\n * @param ttl - 过期时间(毫秒)\n * @returns 持久化的 ref\n */\nexport function usePersistedRefWithTTL<T>(\n key: string,\n defaultValue: T,\n ttl: number,\n): Ref<T> {\n interface StoredData {\n value: T\n expiry: number\n }\n\n const { value } = usePersist<StoredData>({\n key,\n defaultValue: { value: defaultValue, expiry: 0 },\n serialize: (data: unknown) => JSON.stringify(data),\n deserialize: (str: string) => {\n const data = JSON.parse(str) as StoredData\n if (data.expiry && Date.now() > data.expiry) {\n return { value: defaultValue, expiry: 0 }\n }\n return data\n },\n })\n\n // 包装 ref 以自动设置过期时间\n const wrappedRef = ref(value.value.value) as Ref<T>\n\n watch(wrappedRef, (newValue) => {\n value.value = {\n value: newValue,\n expiry: Date.now() + ttl,\n }\n })\n\n return wrappedRef\n}\n\n","/* ============================================================\n * usePersistedState.ts\n * 持久化状态 Composable\n * 提供将 Vue 响应式状态自动持久化到 localStorage/sessionStorage 的能力\n * 支持 TTL 过期、跨标签页同步、防抖保存、序列化自定义等特性\n * ============================================================ */\n\n/* 从 Vue 导入 Ref 类型 */\nimport type {Ref} from 'vue';\n/* 从 store-core 导入防抖和深拷贝工具函数 */\nimport { debounce, deepClone } from '@composy/store-core'\n/* 从 Vue 导入生命周期钩子和响应式 API */\nimport { onUnmounted, ref, watch } from 'vue'\n\n/**\n * 持久化状态配置选项接口\n * @property key - 存储键名\n * @property storage - 存储类型,默认 'localStorage'\n * @property defaultValue - 默认值(当存储中无数据或过期时使用)\n * @serializer - 自定义序列化函数(默认 JSON.stringify)\n * @deserializer - 自定义反序列化函数(默认 JSON.parse)\n * @property syncTabs - 是否同步其他标签页的变化\n * @property debounce - 防抖延迟(毫秒),0 表示不防抖\n * @property deep - 是否深度监听状态变化\n * @property ttl - 存活时间(毫秒),过期后自动清除\n * @property onBeforeChange - 值变化前的钩子,返回 false 可阻止保存\n * @property onAfterChange - 值变化后的钩子\n * @property onBeforeRestore - 恢复前的钩子,可转换恢复的值\n * @property onAfterRestore - 恢复后的钩子\n */\nexport interface PersistedStateOptions<T> {\n key: string\n storage?: 'localStorage' | 'sessionStorage'\n defaultValue?: T\n serializer?: (value: T) => string\n deserializer?: (value: string) => T\n syncTabs?: boolean\n debounce?: number\n deep?: boolean\n ttl?: number\n onBeforeChange?: (newValue: T, oldValue: T) => boolean | void\n onAfterChange?: (newValue: T, oldValue: T) => void\n onBeforeRestore?: (value: T) => T\n onAfterRestore?: (value: T) => void\n}\n\n/**\n * 持久化状态返回值接口\n * @property state - 响应式状态引用\n * @property save - 手动保存到存储\n * @property restore - 从存储手动恢复\n * @property clear - 清除存储中的数据\n * @property reset - 重置为默认值并清除存储\n * @property isExpired - 检查存储数据是否已过期\n */\nexport interface PersistedStateReturn<T> {\n state: Ref<T>\n save: () => void\n restore: () => void\n clear: () => void\n reset: () => void\n isExpired: () => boolean\n}\n\n/**\n * 存储项的内部格式接口\n * 包装序列化后的值,附加时间戳和过期时间\n * @property value - 序列化后的字符串值\n * @property timestamp - 保存时间戳\n * @property expiresAt - 过期时间戳(可选,无则永不过期)\n */\ninterface StorageItem {\n value: string\n timestamp: number\n expiresAt?: number\n}\n\n/**\n * 默认序列化函数\n * 使用 JSON.stringify 将值转为 JSON 字符串\n */\nfunction defaultSerializer<T>(value: T): string {\n return JSON.stringify(value)\n}\n\n/**\n * 默认反序列化函数\n * 使用 JSON.parse 解析 JSON 字符串,解析失败则原样返回\n */\nfunction defaultDeserializer<T>(value: string): T {\n try {\n return JSON.parse(value)\n }\n catch {\n /* 解析失败(数据损坏),原样返回 */\n return value as T\n }\n}\n\n/**\n * 获取浏览器存储对象\n * 在非浏览器环境(如 SSR)返回 null\n * @param type - 存储类型\n * @returns Storage 对象或 null\n */\nfunction getStorage(type: 'localStorage' | 'sessionStorage'): Storage | null {\n /* SSR 环境下无 window 对象 */\n if (typeof window === 'undefined') {\n return null\n }\n\n return type === 'localStorage' ? window.localStorage : window.sessionStorage\n}\n\n/**\n * 持久化状态 Composable\n * 将 Vue 响应式状态自动持久化到浏览器存储\n * 支持 TTL 过期、跨标签页同步、防抖、生命周期钩子\n *\n * @param options - 配置选项\n * @returns 包含状态和操作方法的对象\n */\nexport function usePersistedState<T>(\n options: PersistedStateOptions<T>,\n): PersistedStateReturn<T> {\n /* 解构配置选项,设置默认值 */\n const {\n key, /* 存储键名 */\n storage: storageType = 'localStorage', /* 存储类型,默认 localStorage */\n defaultValue, /* 默认值 */\n serializer = defaultSerializer, /* 序列化函数 */\n deserializer = defaultDeserializer, /* 反序列化函数 */\n syncTabs = false, /* 是否跨标签页同步 */\n debounce: debounceDelay = 0, /* 防抖延迟 */\n deep = true, /* 是否深度监听 */\n ttl, /* 过期时间 */\n onBeforeChange, /* 变化前钩子 */\n onAfterChange, /* 变化后钩子 */\n onBeforeRestore, /* 恢复前钩子 */\n onAfterRestore, /* 恢复后钩子 */\n } = options\n\n /* 获取实际的存储对象 */\n const storage = getStorage(storageType)\n /* 创建默认值的深拷贝工厂函数(避免共享引用) */\n const createDefaultValue = (): T => deepClone(defaultValue as T)\n /* 创建响应式状态,初始值为默认值的深拷贝 */\n const state = ref(createDefaultValue()) as Ref<T>\n\n /**\n * 将当前状态保存到存储\n * 将状态序列化并包装为 StorageItem 格式(含时间戳和过期时间)\n */\n const save = (): void => {\n if (!storage) {\n return /* 存储不可用(SSR 环境) */\n }\n\n try {\n /* 构建存储项:包含序列化值、时间戳和可选的过期时间 */\n const item: StorageItem = {\n value: serializer(state.value), /* 序列化当前状态 */\n timestamp: Date.now(), /* 保存时间 */\n expiresAt: ttl ? Date.now() + ttl : undefined, /* 过期时间 */\n }\n\n /* 将存储项 JSON 序列化后写入存储 */\n storage.setItem(key, JSON.stringify(item))\n }\n catch (error) {\n /* 存储可能已满或被禁用 */\n console.error(`[usePersistedState] 保存失败 (${key}):`, error)\n }\n }\n\n /**\n * 清除存储中的数据\n */\n const clear = (): void => {\n if (!storage) {\n return\n }\n\n try {\n storage.removeItem(key)\n }\n catch (error) {\n console.error(`[usePersistedState] 清除失败 (${key}):`, error)\n }\n }\n\n /**\n * 从存储恢复状态\n * 读取存储项,检查过期,执行恢复前钩子,更新响应式状态\n */\n const restore = (): void => {\n if (!storage) {\n return\n }\n\n try {\n /* 读取存储项 */\n const stored = storage.getItem(key)\n if (!stored) {\n return /* 存储中无数据 */\n }\n\n /* 解析存储项 */\n const item = JSON.parse(stored) as StorageItem\n /* 检查是否过期,过期则清除并退出 */\n if (item.expiresAt && Date.now() > item.expiresAt) {\n clear()\n return\n }\n\n /* 反序列化存储的值 */\n let value = deserializer(item.value)\n /* 执行恢复前钩子,允许转换恢复的值 */\n if (onBeforeRestore) {\n value = onBeforeRestore(value)\n }\n\n /* 更新响应式状态 */\n state.value = value\n /* 执行恢复后钩子 */\n onAfterRestore?.(value)\n }\n catch (error) {\n console.error(`[usePersistedState] 恢复失败 (${key}):`, error)\n }\n }\n\n /**\n * 重置为默认值并清除存储\n */\n const reset = (): void => {\n state.value = createDefaultValue() /* 恢复为默认值 */\n clear() /* 清除存储 */\n }\n\n /**\n * 检查存储数据是否已过期\n * @returns 是否已过期\n */\n const isExpired = (): boolean => {\n /* 无存储或无 TTL 则不可能过期 */\n if (!storage || !ttl) {\n return false\n }\n\n try {\n const stored = storage.getItem(key)\n if (!stored) {\n return false /* 无数据,视为未过期 */\n }\n\n const item = JSON.parse(stored) as StorageItem\n /* 比较当前时间与过期时间 */\n return item.expiresAt ? Date.now() > item.expiresAt : false\n }\n catch {\n return false\n }\n }\n\n /* 如果设置了防抖延迟,创建防抖版的 save 函数 */\n const debouncedSave = debounceDelay > 0 ? debounce(save, debounceDelay) : null\n\n /**\n * 处理跨标签页存储事件\n * 当其他标签页修改了同一 key 的存储时触发\n */\n const handleStorageEvent = (event: StorageEvent): void => {\n /* 只关注目标 key 且有新值的事件 */\n if (event.key !== key || !event.newValue) {\n return\n }\n\n try {\n const item = JSON.parse(event.newValue) as StorageItem\n /* 跳过已过期的数据 */\n if (item.expiresAt && Date.now() > item.expiresAt) {\n return\n }\n\n /* 反序列化并更新本地状态 */\n state.value = deserializer(item.value)\n }\n catch (error) {\n console.error(`[usePersistedState] 同步失败 (${key}):`, error)\n }\n }\n\n /* ── 初始化 ── */\n /* 组件挂载时立即尝试从存储恢复状态 */\n restore()\n\n /* ── 状态变化监听 ── */\n /* 监听 state 变化,自动保存到存储 */\n watch(\n state,\n (newValue, oldValue) => {\n /* 执行变化前钩子,返回 false 可阻止保存 */\n const canContinue = onBeforeChange?.(newValue, oldValue)\n if (canContinue === false) {\n return /* 阻止保存 */\n }\n\n /* 使用防抖保存或立即保存 */\n if (debouncedSave) {\n debouncedSave()\n }\n else {\n save()\n }\n\n /* 执行变化后钩子 */\n onAfterChange?.(newValue, oldValue)\n },\n { deep }, /* 深度监听选项 */\n )\n\n /* ── 跨标签页同步 ── */\n /* 如果启用了跨标签页同步,监听 storage 事件 */\n if (syncTabs && typeof window !== 'undefined') {\n window.addEventListener('storage', handleStorageEvent)\n }\n\n /* ── 清理 ── */\n /* 组件卸载时取消防抖和移除事件监听 */\n onUnmounted(() => {\n /* 取消待执行的防抖保存 */\n debouncedSave?.cancel()\n /* 移除跨标签页同步的事件监听 */\n if (syncTabs && typeof window !== 'undefined') {\n window.removeEventListener('storage', handleStorageEvent)\n }\n })\n\n /* ── 返回状态和方法 ── */\n return {\n state, /* 响应式状态 */\n save, /* 手动保存 */\n restore, /* 手动恢复 */\n clear, /* 清除存储 */\n reset, /* 重置为默认值 */\n isExpired, /* 检查是否过期 */\n }\n}\n","/**\n * useSharedState - 跨标签页状态同步\n *\n * 基于 BroadcastChannel API 实现多标签页实时状态同步\n * @module composables/useSharedState\n */\n\nimport type {Ref} from 'vue';\nimport { deepClone } from '@composy/store-core'\nimport { onUnmounted, ref, watch } from 'vue'\n\n/**\n * useSharedState 选项\n */\nexport interface UseSharedStateOptions {\n /** 是否立即将当前值广播给其他标签页 */\n immediate?: boolean\n /** 是否深度监听变化 */\n deep?: boolean\n}\n\n/**\n * useSharedState 返回值\n */\nexport interface UseSharedStateReturn<T> {\n /** 响应式状态 (双向同步) */\n state: Ref<T>\n /** 手动广播当前状态到其他标签页 */\n broadcast: () => void\n /** 连接的标签页数量 (近似值) */\n peerCount: Ref<number>\n /** 最后同步时间 */\n lastSyncAt: Ref<number | null>\n /** 关闭通道 */\n close: () => void\n}\n\n/**\n * 跨标签页状态同步 Hook\n *\n * 使用 BroadcastChannel API 在多个标签页之间实时同步状态。\n * 任意标签页修改状态后,其他标签页会自动收到更新。\n *\n * @template T - 状态类型 (必须可序列化)\n * @param channelName - 通道名称 (相同名称的标签页共享状态)\n * @param initialState - 初始状态\n * @param options - 选项\n * @returns 同步状态和控制 API\n *\n * @example\n * ```typescript\n * // 标签页 A\n * const { state } = useSharedState('my-channel', { count: 0, user: 'Alice' })\n * state.value.count++ // 所有标签页都会收到更新\n *\n * // 标签页 B (相同代码)\n * const { state } = useSharedState('my-channel', { count: 0, user: 'Alice' })\n * // state.value.count 会自动变为 1\n *\n * // 手动广播\n * broadcast()\n *\n * // 关闭通道\n * close()\n * ```\n */\nexport function useSharedState<T>(\n channelName: string,\n initialState: T,\n options: UseSharedStateOptions = {},\n): UseSharedStateReturn<T> {\n const { immediate = false, deep = true } = options\n\n const state = ref<T>(deepClone(initialState)) as Ref<T>\n const peerCount = ref(0)\n const lastSyncAt = ref<number | null>(null)\n\n let channel: BroadcastChannel | null = null\n let isReceiving = false\n\n // 检测 BroadcastChannel 是否可用\n if (typeof BroadcastChannel === 'undefined') {\n console.warn('[useSharedState] BroadcastChannel not supported in this environment')\n return {\n state,\n broadcast: () => {},\n peerCount,\n lastSyncAt,\n close: () => {},\n }\n }\n\n try {\n channel = new BroadcastChannel(channelName)\n }\n catch (e) {\n console.warn('[useSharedState] Failed to create BroadcastChannel:', e)\n return {\n state,\n broadcast: () => {},\n peerCount,\n lastSyncAt,\n close: () => {},\n }\n }\n\n /** 广播当前状态 */\n function broadcast(): void {\n if (!channel) return\n try {\n channel.postMessage({\n type: 'state-update',\n payload: deepClone(state.value),\n timestamp: Date.now(),\n })\n }\n catch (e) {\n console.warn('[useSharedState] broadcast error:', e)\n }\n }\n\n /** 接收其他标签页的消息 */\n channel.onmessage = (event: MessageEvent) => {\n const msg = event.data\n if (!msg || typeof msg !== 'object') return\n\n if (msg.type === 'state-update') {\n isReceiving = true\n state.value = msg.payload as T\n lastSyncAt.value = msg.timestamp || Date.now()\n isReceiving = false\n }\n else if (msg.type === 'ping') {\n channel?.postMessage({ type: 'pong' })\n }\n else if (msg.type === 'pong') {\n peerCount.value++\n }\n }\n\n /** 监听本地变化并广播 */\n const stopWatch = watch(\n state,\n () => {\n if (!isReceiving) {\n broadcast()\n }\n },\n { deep },\n )\n\n /** 关闭通道 */\n function close(): void {\n stopWatch()\n if (channel) {\n channel.close()\n channel = null\n }\n }\n\n // 通知其他标签页本页存在\n if (immediate) {\n broadcast()\n }\n\n // 发送 ping 探测其他标签页\n if (channel) {\n peerCount.value = 0\n channel.postMessage({ type: 'ping' })\n }\n\n onUnmounted(() => {\n close()\n })\n\n return {\n state,\n broadcast,\n peerCount,\n lastSyncAt,\n close,\n }\n}\n","/**\n * useStore Composable\n *\n * 提供在组件中使用 Store 的便捷方法\n * @module composables/useStore\n */\n\nimport type { StoreGeneric } from 'pinia'\nimport type { Ref } from 'vue'\nimport { storeToRefs } from 'pinia'\nimport { computed, ref, watch } from 'vue'\n\n/**\n * useStore 选项\n */\nexport interface UseStoreOptions {\n /** 是否立即同步状态 */\n immediate?: boolean\n /** 是否深度监听 */\n deep?: boolean\n}\n\n/**\n * 通用 Store Hook\n *\n * 提供对 Pinia Store 的便捷访问\n *\n * @template T - Store 类型\n * @param useStoreHook - Pinia useStore hook\n * @param _options - 选项\n * @returns Store 包装对象\n *\n * @example\n * ```typescript\n * // 定义 Store\n * const useCounterStore = defineStore('counter', {\n * state: () => ({ count: 0 }),\n * actions: {\n * increment() { this.count++ }\n * }\n * })\n *\n * // 在组件中使用\n * const { store, refs, reset } = useStore(useCounterStore)\n *\n * // 访问响应式状态\n * console.log(refs.count.value)\n *\n * // 调用 action\n * store.increment()\n * ```\n */\nexport function useStore<T extends StoreGeneric>(\n useStoreHook: () => T,\n _options: UseStoreOptions = {},\n): {\n store: T\n refs: ReturnType<typeof storeToRefs<T>>\n reset: () => void\n patch: (partial: Partial<T> | ((state: T) => void)) => void\n} {\n const store = useStoreHook()\n\n // 获取响应式 refs\n const refs = storeToRefs(store)\n\n // 重置函数\n const reset = (): void => {\n if ('$reset' in store && typeof store.$reset === 'function') {\n (store.$reset as () => void)()\n }\n }\n\n // 批量更新函数\n const patch = (partial: Partial<T> | ((state: T) => void)): void => {\n if ('$patch' in store && typeof store.$patch === 'function') {\n (store.$patch as (partial: unknown) => void)(partial)\n }\n }\n\n return {\n store,\n refs,\n reset,\n patch,\n }\n}\n\n/**\n * 创建简单的响应式 Store\n *\n * 不依赖 Pinia,适用于简单场景\n *\n * @template S - 状态类型\n * @param initialState - 初始状态\n * @returns 响应式 Store\n *\n * @example\n * ```typescript\n * const { state, setState, reset } = useSimpleStore({\n * count: 0,\n * name: 'test'\n * })\n *\n * // 更新状态\n * setState({ count: 1 })\n *\n * // 重置\n * reset()\n * ```\n */\nexport function useSimpleStore<S extends Record<string, unknown>>(\n initialState: S,\n): {\n state: Ref<S>\n setState: (partial: Partial<S>) => void\n reset: () => void\n subscribe: (callback: (state: S) => void) => () => void\n} {\n const state = ref({ ...initialState }) as Ref<S>\n const initialStateCopy = { ...initialState }\n\n const setState = (partial: Partial<S>): void => {\n Object.assign(state.value, partial)\n }\n\n const reset = (): void => {\n state.value = { ...initialStateCopy } as S\n }\n\n const subscribe = (callback: (state: S) => void): (() => void) => {\n const stop = watch(\n state,\n (newState) => {\n callback(newState)\n },\n { deep: true },\n )\n return stop\n }\n\n return {\n state,\n setState,\n reset,\n subscribe,\n }\n}\n\n/**\n * 创建计算属性 Store\n *\n * @template S - 状态类型\n * @template G - Getters 类型\n * @param state - 状态\n * @param getters - 计算属性\n * @returns 带计算属性的 Store\n */\nexport function useComputedStore<\n S extends Record<string, unknown>,\n G extends Record<string, (state: S) => unknown>,\n>(\n state: Ref<S>,\n getters: G,\n): {\n state: Ref<S>\n getters: { [K in keyof G]: ReturnType<G[K]> }\n} {\n const computedGetters = {} as { [K in keyof G]: ReturnType<G[K]> }\n\n for (const key in getters) {\n (computedGetters as Record<string, unknown>)[key] = computed(() =>\n getters[key](state.value),\n )\n }\n\n return {\n state,\n getters: computedGetters,\n }\n}\n\n","import type { EventData } from '@composy/store-core'\nimport type { Pinia, StoreGeneric } from 'pinia'\nimport type { Ref } from 'vue'\nimport { deepClone, globalEventBus } from '@composy/store-core'\nimport { getActivePinia } from 'pinia'\nimport { onUnmounted, ref } from 'vue'\n\nconst STORE_REFRESH_EVENTS = new Set([\n 'store:created',\n 'store:disposed',\n 'state:changed',\n])\n\ntype PiniaWithStores = Pinia & {\n _s?: Map<string, StoreGeneric>\n}\n\nexport interface StoreInfo {\n id: string\n state: Record<string, unknown>\n active: boolean\n}\n\nexport interface EventLogEntry {\n type: string\n payload: unknown\n timestamp: number\n time: string\n}\n\nexport interface UseStoreDevtoolsReturn {\n stores: Ref<StoreInfo[]>\n eventLog: Ref<EventLogEntry[]>\n isMonitoring: Ref<boolean>\n startMonitoring: () => void\n stopMonitoring: () => void\n clearLog: () => void\n refreshStores: () => void\n getStoreState: (id: string) => Record<string, unknown> | null\n resetStore: (id: string) => boolean\n resetAllStores: () => void\n}\n\nfunction formatTime(timestamp: number): string {\n const date = new Date(timestamp)\n const hours = date.getHours().toString().padStart(2, '0')\n const minutes = date.getMinutes().toString().padStart(2, '0')\n const seconds = date.getSeconds().toString().padStart(2, '0')\n const milliseconds = date.getMilliseconds().toString().padStart(3, '0')\n\n return `${hours}:${minutes}:${seconds}.${milliseconds}`\n}\n\nfunction getPinia(): PiniaWithStores | null {\n return (getActivePinia() ?? null) as PiniaWithStores | null\n}\n\nfunction snapshotStore(store: StoreGeneric, id: string): StoreInfo {\n return {\n id,\n state: deepClone(store.$state),\n active: true,\n }\n}\n\nexport function useStoreDevtools(options: {\n /**\n * 最多保留多少条事件日志。\n */\n maxLogEntries?: number\n} = {}): UseStoreDevtoolsReturn {\n const { maxLogEntries = 200 } = options\n\n const stores = ref<StoreInfo[]>([])\n const eventLog = ref<EventLogEntry[]>([])\n const isMonitoring = ref(false)\n\n let unsubscribe: (() => void) | null = null\n\n function refreshStores(): void {\n const pinia = getPinia()\n if (!pinia?._s) {\n stores.value = []\n return\n }\n\n stores.value = Array.from(pinia._s.entries(), ([id, store]) => {\n return snapshotStore(store, id)\n })\n }\n\n function startMonitoring(): void {\n if (isMonitoring.value) {\n return\n }\n\n isMonitoring.value = true\n unsubscribe = globalEventBus.on('*', (data: EventData) => {\n const entry: EventLogEntry = {\n type: data.type,\n payload: data.payload,\n timestamp: data.timestamp,\n time: formatTime(data.timestamp),\n }\n\n eventLog.value = [entry, ...eventLog.value].slice(0, maxLogEntries)\n\n if (STORE_REFRESH_EVENTS.has(data.type)) {\n refreshStores()\n }\n })\n }\n\n function stopMonitoring(): void {\n if (!isMonitoring.value) {\n return\n }\n\n isMonitoring.value = false\n unsubscribe?.()\n unsubscribe = null\n }\n\n function clearLog(): void {\n eventLog.value = []\n }\n\n function getStoreState(id: string): Record<string, unknown> | null {\n const pinia = getPinia()\n const store = pinia?._s?.get(id)\n\n if (!store) {\n return null\n }\n\n return deepClone(store.$state)\n }\n\n function resetStore(id: string): boolean {\n const pinia = getPinia()\n const store = pinia?._s?.get(id)\n\n if (!store || typeof store.$reset !== 'function') {\n return false\n }\n\n store.$reset()\n refreshStores()\n return true\n }\n\n function resetAllStores(): void {\n const pinia = getPinia()\n if (!pinia?._s) {\n return\n }\n\n pinia._s.forEach((store) => {\n if (typeof store.$reset === 'function') {\n store.$reset()\n }\n })\n\n refreshStores()\n }\n\n refreshStores()\n\n onUnmounted(() => {\n stopMonitoring()\n })\n\n return {\n stores,\n eventLog,\n isMonitoring,\n startMonitoring,\n stopMonitoring,\n clearLog,\n refreshStores,\n getStoreState,\n resetStore,\n resetAllStores,\n }\n}\n","/**\n * useStoreSnapshot - Store 状态快照工具\n *\n * 提供状态快照、历史记录和调试功能\n * @module composables/useStoreSnapshot\n */\n\nimport type {ComputedRef, Ref} from 'vue';\nimport { deepClone, deepEqual } from '@composy/store-core'\nimport { computed, onUnmounted, ref, watch } from 'vue'\n\n/**\n * 状态快照\n */\nexport interface StateSnapshot<S = unknown> {\n /** 快照 ID */\n id: string\n /** 状态数据 */\n state: S\n /** 时间戳 */\n timestamp: number\n /** 快照标签 */\n label?: string\n /** 元数据 */\n meta?: Record<string, unknown>\n}\n\n/**\n * 状态差异\n */\nexport interface StateDiff {\n /** 差异路径 */\n path: string\n /** 旧值 */\n oldValue: unknown\n /** 新值 */\n newValue: unknown\n /** 差异类型 */\n type: 'add' | 'remove' | 'change'\n}\n\n/**\n * useStoreSnapshot 选项\n */\nexport interface UseStoreSnapshotOptions {\n /** 最大快照数量 */\n maxSnapshots?: number\n /** 是否自动记录 */\n autoRecord?: boolean\n /** 自动记录防抖延迟(毫秒) */\n debounceMs?: number\n /** 是否深度监听 */\n deep?: boolean\n}\n\n/**\n * useStoreSnapshot 返回值\n */\nexport interface UseStoreSnapshotReturn<S> {\n /** 快照列表 */\n snapshots: Ref<Array<StateSnapshot<S>>>\n /** 当前快照索引 */\n currentIndex: Ref<number>\n /** 是否可以撤销 */\n canUndo: ComputedRef<boolean>\n /** 是否可以重做 */\n canRedo: ComputedRef<boolean>\n /** 创建快照 */\n takeSnapshot: (label?: string, meta?: Record<string, unknown>) => StateSnapshot<S>\n /** 恢复快照 */\n restoreSnapshot: (id: string) => boolean\n /** 撤销 */\n undo: () => boolean\n /** 重做 */\n redo: () => boolean\n /** 跳转到指定快照 */\n goto: (index: number) => boolean\n /** 清空快照 */\n clearSnapshots: () => void\n /** 获取状态差异 */\n getDiff: (snapshotId1: string, snapshotId2: string) => StateDiff[]\n /** 导出快照 */\n exportSnapshots: () => string\n /** 导入快照 */\n importSnapshots: (data: string) => boolean\n}\n\n/**\n * 生成唯一 ID\n */\nfunction generateId(): string {\n return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`\n}\n\n/**\n * 计算两个对象的差异\n */\nfunction computeDiff(\n oldObj: unknown,\n newObj: unknown,\n path = '',\n): StateDiff[] {\n const diffs: StateDiff[] = []\n\n // 类型不同\n if (typeof oldObj !== typeof newObj) {\n diffs.push({\n path: path || '.',\n oldValue: oldObj,\n newValue: newObj,\n type: 'change',\n })\n return diffs\n }\n\n // 基础类型比较\n if (typeof oldObj !== 'object' || oldObj === null || newObj === null) {\n if (!Object.is(oldObj, newObj)) {\n diffs.push({\n path: path || '.',\n oldValue: oldObj,\n newValue: newObj,\n type: 'change',\n })\n }\n return diffs\n }\n\n // 数组比较\n if (Array.isArray(oldObj) && Array.isArray(newObj)) {\n const maxLen = Math.max(oldObj.length, newObj.length)\n for (let i = 0; i < maxLen; i++) {\n const itemPath = path ? `${path}[${i}]` : `[${i}]`\n if (i >= oldObj.length) {\n diffs.push({\n path: itemPath,\n oldValue: undefined,\n newValue: newObj[i],\n type: 'add',\n })\n }\n else if (i >= newObj.length) {\n diffs.push({\n path: itemPath,\n oldValue: oldObj[i],\n newValue: undefined,\n type: 'remove',\n })\n }\n else {\n diffs.push(...computeDiff(oldObj[i], newObj[i], itemPath))\n }\n }\n return diffs\n }\n\n // 对象比较\n const oldKeys = new Set(Object.keys(oldObj as Record<string, unknown>))\n const newKeys = new Set(Object.keys(newObj as Record<string, unknown>))\n\n // 检查新增的键\n for (const key of newKeys) {\n if (!oldKeys.has(key)) {\n diffs.push({\n path: path ? `${path}.${key}` : key,\n oldValue: undefined,\n newValue: (newObj as Record<string, unknown>)[key],\n type: 'add',\n })\n }\n }\n\n // 检查删除的键\n for (const key of oldKeys) {\n if (!newKeys.has(key)) {\n diffs.push({\n path: path ? `${path}.${key}` : key,\n oldValue: (oldObj as Record<string, unknown>)[key],\n newValue: undefined,\n type: 'remove',\n })\n }\n }\n\n // 检查修改的键\n for (const key of oldKeys) {\n if (newKeys.has(key)) {\n diffs.push(\n ...computeDiff(\n (oldObj as Record<string, unknown>)[key],\n (newObj as Record<string, unknown>)[key],\n path ? `${path}.${key}` : key,\n ),\n )\n }\n }\n\n return diffs\n}\n\n/**\n * Store 状态快照 Hook\n *\n * 提供状态快照、撤销/重做和调试功能\n *\n * @template S - 状态类型\n * @param state - 响应式状态\n * @param setState - 状态设置函数\n * @param options - 配置选项\n * @returns 快照操作对象\n *\n * @example\n * ```typescript\n * const store = useCounterStore()\n *\n * const {\n * snapshots,\n * canUndo,\n * canRedo,\n * takeSnapshot,\n * undo,\n * redo,\n * getDiff\n * } = useStoreSnapshot(\n * () => store.$state,\n * (state) => store.$patch(state),\n * { maxSnapshots: 50, autoRecord: true }\n * )\n *\n * // 手动创建快照\n * takeSnapshot('保存点 1')\n *\n * // 撤销操作\n * if (canUndo.value) {\n * undo()\n * }\n *\n * // 获取状态差异\n * const diff = getDiff(snapshots.value[0].id, snapshots.value[1].id)\n * ```\n */\nexport function useStoreSnapshot<S>(\n state: Ref<S> | (() => S),\n setState: (state: S) => void,\n options: UseStoreSnapshotOptions = {},\n): UseStoreSnapshotReturn<S> {\n const {\n maxSnapshots = 100,\n autoRecord = false,\n debounceMs = 300,\n deep = true,\n } = options\n\n const snapshots = ref<Array<StateSnapshot<S>>>([]) as Ref<Array<StateSnapshot<S>>>\n const currentIndex = ref(-1)\n let debounceTimer: ReturnType<typeof setTimeout> | undefined\n let isRestoring = false\n\n // 获取当前状态\n const getState = (): S => {\n return typeof state === 'function' ? state() : state.value\n }\n\n // 是否可以撤销\n const canUndo = computed(() => currentIndex.value > 0)\n\n // 是否可以重做\n const canRedo = computed(() => currentIndex.value < snapshots.value.length - 1)\n\n /**\n * 创建快照\n */\n const takeSnapshot = (label?: string, meta?: Record<string, unknown>): StateSnapshot<S> => {\n const currentState = getState()\n const snapshot: StateSnapshot<S> = {\n id: generateId(),\n state: deepClone(currentState),\n timestamp: Date.now(),\n label,\n meta,\n }\n\n // 如果当前不在最新位置,删除后面的快照\n if (currentIndex.value < snapshots.value.length - 1) {\n snapshots.value = snapshots.value.slice(0, currentIndex.value + 1)\n }\n\n // 添加新快照\n snapshots.value.push(snapshot)\n currentIndex.value = snapshots.value.length - 1\n\n // 限制快照数量\n if (snapshots.value.length > maxSnapshots) {\n snapshots.value.shift()\n currentIndex.value--\n }\n\n return snapshot\n }\n\n /**\n * 恢复快照\n */\n const restoreSnapshot = (id: string): boolean => {\n const index = snapshots.value.findIndex(s => s.id === id)\n if (index === -1) return false\n\n isRestoring = true\n try {\n setState(deepClone(snapshots.value[index].state))\n currentIndex.value = index\n return true\n }\n finally {\n isRestoring = false\n }\n }\n\n /**\n * 撤销\n */\n const undo = (): boolean => {\n if (!canUndo.value) return false\n\n currentIndex.value--\n isRestoring = true\n try {\n setState(deepClone(snapshots.value[currentIndex.value].state))\n return true\n }\n finally {\n isRestoring = false\n }\n }\n\n /**\n * 重做\n */\n const redo = (): boolean => {\n if (!canRedo.value) return false\n\n currentIndex.value++\n isRestoring = true\n try {\n setState(deepClone(snapshots.value[currentIndex.value].state))\n return true\n }\n finally {\n isRestoring = false\n }\n }\n\n /**\n * 跳转到指定快照\n */\n const goto = (index: number): boolean => {\n if (index < 0 || index >= snapshots.value.length) return false\n\n currentIndex.value = index\n isRestoring = true\n try {\n setState(deepClone(snapshots.value[index].state))\n return true\n }\n finally {\n isRestoring = false\n }\n }\n\n /**\n * 清空快照\n */\n const clearSnapshots = (): void => {\n snapshots.value = []\n currentIndex.value = -1\n }\n\n /**\n * 获取状态差异\n */\n const getDiff = (snapshotId1: string, snapshotId2: string): StateDiff[] => {\n const snapshot1 = snapshots.value.find(s => s.id === snapshotId1)\n const snapshot2 = snapshots.value.find(s => s.id === snapshotId2)\n\n if (!snapshot1 || !snapshot2) return []\n\n return computeDiff(snapshot1.state, snapshot2.state)\n }\n\n /**\n * 导出快照\n */\n const exportSnapshots = (): string => {\n return JSON.stringify({\n snapshots: snapshots.value,\n currentIndex: currentIndex.value,\n exportedAt: Date.now(),\n })\n }\n\n /**\n * 导入快照\n */\n const importSnapshots = (data: string): boolean => {\n try {\n const parsed = JSON.parse(data) as {\n snapshots: Array<StateSnapshot<S>>\n currentIndex: number\n }\n\n if (!Array.isArray(parsed.snapshots)) return false\n\n snapshots.value = parsed.snapshots\n currentIndex.value = Math.min(\n parsed.currentIndex ?? snapshots.value.length - 1,\n snapshots.value.length - 1,\n )\n\n return true\n }\n catch {\n return false\n }\n }\n\n // 自动记录\n if (autoRecord) {\n const stateRef = typeof state === 'function' ? computed(state) : state\n\n const stopWatch = watch(\n stateRef,\n (newState, oldState) => {\n // 如果是恢复操作,不记录\n if (isRestoring) return\n\n // 如果状态未变化,不记录\n if (deepEqual(newState, oldState)) return\n\n // 防抖记录\n if (debounceTimer) {\n clearTimeout(debounceTimer)\n }\n\n debounceTimer = setTimeout(() => {\n takeSnapshot()\n }, debounceMs)\n },\n { deep },\n )\n\n // 初始快照\n takeSnapshot('初始状态')\n\n onUnmounted(() => {\n stopWatch()\n if (debounceTimer) {\n clearTimeout(debounceTimer)\n }\n })\n }\n\n return {\n snapshots,\n currentIndex,\n canUndo,\n canRedo,\n takeSnapshot,\n restoreSnapshot,\n undo,\n redo,\n goto,\n clearSnapshots,\n getDiff,\n exportSnapshots,\n importSnapshots,\n }\n}\n\n","/**\n * useSubscribe Composable\n *\n * 提供 Store 订阅功能\n * @module composables/useSubscribe\n */\n\nimport type { Ref, WatchStopHandle } from 'vue'\nimport type { MutationInfo, SubscribeOptions } from '../types'\nimport { onUnmounted, watch } from 'vue'\n\n/**\n * 订阅 Store 状态变化\n *\n * @template S - 状态类型\n * @param store - Pinia Store 实例\n * @param callback - 回调函数\n * @param options - 订阅选项\n * @returns 取消订阅函数\n *\n * @example\n * ```typescript\n * const store = useCounterStore()\n *\n * // 订阅状态变化\n * const unsubscribe = useSubscribe(store, (mutation, state) => {\n * console.log('State changed:', mutation.type, state)\n * })\n *\n * // 取消订阅\n * unsubscribe()\n * ```\n */\nexport function useSubscribe<S extends Record<string, unknown>>(\n store: S & { $subscribe?: (callback: (mutation: MutationInfo<S>, state: S) => void, options?: { detached?: boolean }) => () => void },\n callback: (mutation: MutationInfo<S>, state: S) => void,\n options: SubscribeOptions = {},\n): () => void {\n const { detached = false } = options\n\n // 使用 Pinia 的 $subscribe\n if (store.$subscribe) {\n const unsubscribe = store.$subscribe(\n (mutation, state) => {\n callback(mutation as MutationInfo<S>, state as S)\n },\n { detached },\n )\n\n // 组件卸载时自动取消订阅\n if (!detached) {\n onUnmounted(() => {\n unsubscribe()\n })\n }\n\n return unsubscribe\n }\n\n // 如果不是 Pinia Store,返回空函数\n console.warn('[useSubscribe] Store 不支持 $subscribe 方法')\n return () => {}\n}\n\n/**\n * 订阅 Store Action 调用\n *\n * @template S - Store 类型\n * @param store - Pinia Store 实例\n * @param callback - 回调函数\n * @returns 取消订阅函数\n *\n * @example\n * ```typescript\n * const store = useCounterStore()\n *\n * // 订阅 Action 调用\n * useOnAction(store, ({ name, args, after, onError }) => {\n * console.log(`Action ${name} called with:`, args)\n *\n * after((result) => {\n * console.log(`Action ${name} finished with:`, result)\n * })\n *\n * onError((error) => {\n * console.error(`Action ${name} failed:`, error)\n * })\n * })\n * ```\n */\nexport function useOnAction<S extends Record<string, unknown>>(\n store: S & { $onAction?: (callback: (context: ActionContext) => void, detached?: boolean) => () => void },\n callback: (context: ActionContext) => void,\n detached = false,\n): () => void {\n if (store.$onAction) {\n const unsubscribe = store.$onAction(callback, detached)\n\n if (!detached) {\n onUnmounted(() => {\n unsubscribe()\n })\n }\n\n return unsubscribe\n }\n\n console.warn('[useOnAction] Store 不支持 $onAction 方法')\n return () => {}\n}\n\n/**\n * Action 上下文\n */\nexport interface ActionContext {\n /** Action 名称 */\n name: string\n /** Store 实例 */\n store: unknown\n /** Action 参数 */\n args: unknown[]\n /** Action 完成后回调 */\n after: (callback: (result: unknown) => void) => void\n /** Action 错误回调 */\n onError: (callback: (error: unknown) => void) => void\n}\n\n/**\n * 监听 ref 变化\n *\n * @template T - 值类型\n * @param source - 监听源\n * @param callback - 回调函数\n * @param options - 监听选项\n * @returns 停止监听函数\n */\nexport function useWatch<T>(\n source: Ref<T>,\n callback: (newValue: T, oldValue: T | undefined) => void,\n options: SubscribeOptions = {},\n): WatchStopHandle {\n const { immediate = false, deep = false, detached = false } = options\n\n const stop = watch(\n source,\n (newValue, oldValue) => {\n callback(newValue, oldValue)\n },\n { immediate, deep },\n )\n\n if (!detached) {\n onUnmounted(() => {\n stop()\n })\n }\n\n return stop\n}\n\n","// 从 pinia 导入 Pinia 类型\nimport type { Pinia } from 'pinia'\n// 从 vue 导入 InjectionKey 类型,用于类型安全的依赖注入\nimport type { InjectionKey } from 'vue'\n// 导入 StoreRuntime 类型,表示 store 运行时实例\nimport type { StoreRuntime } from '../runtime/store-runtime'\n// 导入 GlobalStoreAPI 类型,表示全局 store API 接口\nimport type { GlobalStoreAPI } from '../types'\n\n// store 全局 API 的注入键符号,用于 provide/inject 传递 GlobalStoreAPI 实例\nexport const STORE_API_SYMBOL = Symbol('ldesign-store-api') as InjectionKey<GlobalStoreAPI>\n// store Pinia 实例的注入键符号,用于 provide/inject 传递 Pinia 实例\nexport const STORE_PINIA_SYMBOL = Symbol('ldesign-store-pinia') as InjectionKey<Pinia>\n// store 运行时实例的注入键符号,用于 provide/inject 传递 StoreRuntime 实例\nexport const STORE_RUNTIME_SYMBOL = Symbol('ldesign-store-runtime') as InjectionKey<StoreRuntime>\n","/* ============================================================\n * pinia-plugin.ts\n * Pinia 持久化插件\n * 提供将 Pinia Store 状态自动持久化到浏览器存储的能力\n * 支持 TTL 过期、跨标签页同步、include/exclude 路径过滤、\n * 防抖保存、$dispose 生命周期钩子等特性\n * ============================================================ */\n\n/* 从 Pinia 导入插件相关类型 */\nimport type { PiniaPlugin, PiniaPluginContext, StoreGeneric } from 'pinia'\n/* 从本地类型文件导入持久化选项 */\nimport type { PersistOptions } from '../types'\n/* 从 store-core 导入工具函数 */\nimport { debounce, deepClone, deepMerge, deleteByPath, getByPath, setByPath } from '@composy/store-core'\n\n/* 用于标记 store 的 $dispose 是否已被 patch 的 Symbol 键 */\nconst STORE_PERSIST_DISPOSE_KEY = Symbol('ldesign-store-persist-dispose')\n\n/* 浏览器存储类型 */\ntype BrowserStorage = 'localStorage' | 'sessionStorage'\n\n/* 可被 patch $dispose 的 Store 扩展类型 */\ntype DisposableStore = StoreGeneric & {\n [STORE_PERSIST_DISPOSE_KEY]?: () => void /* 恢复原始 $dispose 的函数 */\n}\n\n/**\n * 持久化存储项的内部格式接口\n * 包含序列化的状态、时间戳、过期时间和版本号\n */\ninterface PersistedStorageItem {\n state: string /* 序列化后的状态字符串 */\n timestamp: number /* 保存时间戳 */\n expiresAt?: number /* 过期时间戳(可选) */\n version: number /* 数据格式版本号 */\n}\n\n/**\n * 持久化插件控制器接口\n * @property plugin - Pinia 插件函数\n * @property dispose - 清理函数,用于移除所有持久化订阅\n */\ninterface PersistPluginController {\n plugin: PiniaPlugin\n dispose: () => void\n}\n\n/**\n * 持久化插件全局配置选项\n * @property storage - 默认存储类型\n * @property keyPrefix - 存储键名前缀\n * @property debounce - 防抖延迟(毫秒)\n * @property debug - 是否开启调试日志\n * @property syncTabs - 是否默认启用跨标签页同步\n * @property defaultTTL - 默认过期时间(毫秒)\n */\nexport interface PersistPluginOptions {\n storage?: BrowserStorage\n keyPrefix?: string\n debounce?: number\n debug?: boolean\n syncTabs?: boolean\n defaultTTL?: number\n}\n\n/* 增强版持久化插件选项(与基础版相同,预留扩展) */\nexport interface EnhancedPersistPluginOptions extends PersistPluginOptions {}\n\n/**\n * 增强版持久化选项(Store 级别配置)\n * 在基础 PersistOptions 上增加 excludePaths、syncTabs、ttl\n */\nexport interface EnhancedPersistOptions extends PersistOptions {\n syncTabs?: boolean\n ttl?: number\n excludePaths?: string[]\n}\n\n/* 活跃的持久化控制器集合,用于全局清理 */\nconst activePersistControllers = new Set<() => void>()\n\n/**\n * 获取浏览器存储对象\n * 在 SSR 环境下返回 null\n */\nfunction getStorage(type: BrowserStorage): Storage | null {\n if (typeof window === 'undefined') {\n return null\n }\n\n return type === 'localStorage' ? window.localStorage : window.sessionStorage\n}\n\n/**\n * 规范化 Store 的 persist 配置\n * 将 boolean/对象/undefined 统一为 EnhancedPersistOptions | null\n */\nfunction normalizePersistConfig(\n value: boolean | PersistOptions | EnhancedPersistOptions | undefined,\n): EnhancedPersistOptions | null {\n if (!value) {\n return null /* 未配置持久化 */\n }\n\n /* boolean true 表示使用默认配置的空对象 */\n return typeof value === 'boolean' ? {} : value\n}\n\n/**\n * 根据 include/exclude 路径过滤状态\n * 优先使用 includePaths(白名单),其次使用 excludePaths(黑名单)\n *\n * @param state - 完整状态对象\n * @param includePaths - 包含路径列表(白名单模式)\n * @param excludePaths - 排除路径列表(黑名单模式)\n * @returns 过滤后的状态对象\n */\nfunction filterState(\n state: Record<string, unknown>,\n includePaths?: string[],\n excludePaths?: string[],\n): Record<string, unknown> {\n /* 白名单模式:只保留指定路径 */\n if (includePaths?.length) {\n const pickedState: Record<string, unknown> = {}\n includePaths.forEach((path) => {\n const value = getByPath(state, path)\n if (value !== undefined) {\n setByPath(pickedState, path, deepClone(value))\n }\n })\n return pickedState\n }\n\n /* 黑名单模式:排除指定路径 */\n if (excludePaths?.length) {\n const nextState = deepClone(state)\n excludePaths.forEach((path) => {\n deleteByPath(nextState, path)\n })\n return nextState\n }\n\n /* 无过滤:返回状态的深拷贝 */\n return deepClone(state)\n}\n\n/**\n * 将持久化的状态恢复到 Store\n * 使用 $patch + deepMerge 避免覆盖整个 state 引用\n */\nfunction restoreStoreState(\n store: StoreGeneric,\n nextState: Record<string, unknown>,\n): void {\n store.$patch((state) => {\n deepMerge(state as Record<string, unknown>, nextState)\n })\n}\n\n/**\n * 为 Store 的 $dispose 方法打补丁\n * 在 Store 被销毁时自动执行清理逻辑(取消订阅、移除事件监听等)\n *\n * @param store - 目标 Store\n * @param cleanup - 清理回调函数\n */\nfunction patchPersistDispose(store: DisposableStore, cleanup: () => void): void {\n /* 如果之前已有补丁,先恢复原始方法 */\n const restorePatchedDispose = store[STORE_PERSIST_DISPOSE_KEY]\n if (restorePatchedDispose) {\n restorePatchedDispose()\n }\n\n /* 保存原始 $dispose 方法 */\n const originalDispose = store.$dispose\n /* 替换为带清理逻辑的版本 */\n store.$dispose = function patchedDispose() {\n cleanup() /* 先执行清理 */\n return originalDispose.call(this) /* 再调用原始 dispose */\n }\n\n /* 保存恢复函数,供后续补丁使用 */\n store[STORE_PERSIST_DISPOSE_KEY] = () => {\n store.$dispose = originalDispose\n delete store[STORE_PERSIST_DISPOSE_KEY]\n }\n}\n\n/**\n * 创建持久化插件控制器\n * 返回一个 Pinia 插件函数和对应的 dispose 清理函数\n *\n * @param options - 插件全局配置\n * @returns 控制器对象(包含 plugin 和 dispose)\n */\nexport function createPersistPluginController(\n options: PersistPluginOptions = {},\n): PersistPluginController {\n /* 解构配置选项,设置默认值 */\n const {\n storage: defaultStorage = 'localStorage', /* 默认存储类型 */\n keyPrefix = 'ldesign-store:', /* 存储键前缀 */\n debounce: debounceDelay = 100, /* 默认 100ms 防抖 */\n debug = false, /* 调试模式 */\n syncTabs: defaultSyncTabs = false, /* 默认不跨标签页同步 */\n defaultTTL, /* 默认 TTL */\n } = options\n\n /* 清理函数映射表(key → cleanup) */\n const cleanupMap = new Map<string, () => void>()\n\n /**\n * 全局 dispose 函数\n * 清理所有 Store 的持久化订阅\n */\n const dispose = (): void => {\n cleanupMap.forEach(cleanup => cleanup())\n cleanupMap.clear()\n activePersistControllers.delete(dispose)\n }\n\n /* 注册到活跃控制器集合 */\n activePersistControllers.add(dispose)\n\n /**\n * Pinia 插件函数\n * 每当创建/使用一个 Pinia Store 时自动调用\n */\n const plugin: PiniaPlugin = (context: PiniaPluginContext): void => {\n const { store, options: storeOptions } = context\n /* 规范化该 Store 的持久化配置 */\n const config = normalizePersistConfig(\n (storeOptions as { persist?: boolean | PersistOptions | EnhancedPersistOptions }).persist,\n )\n\n /* 该 Store 未配置持久化则跳过 */\n if (!config) {\n return\n }\n\n /* 解析该 Store 的存储配置(Store 级别配置优先于全局配置) */\n const storageType = config.storage === 'sessionStorage' ? 'sessionStorage' : defaultStorage\n const storage = getStorage(storageType)\n const serializer = config.serializer?.serialize ?? JSON.stringify /* 序列化函数 */\n const deserialize = config.serializer?.deserialize ?? JSON.parse /* 反序列化函数 */\n const syncTabs = config.syncTabs ?? defaultSyncTabs /* 跨标签页同步 */\n const ttl = config.ttl ?? defaultTTL /* 过期时间 */\n const key = `${keyPrefix}${config.key || store.$id}` /* 存储键名 */\n\n /* 存储不可用则跳过 */\n if (!storage) {\n if (debug) {\n console.warn(`[PersistPlugin] Storage 不可用 (${store.$id})`)\n }\n return\n }\n\n /* 标记:是否正在从存储恢复状态(防止恢复过程中触发保存) */\n let isRestoring = false\n\n /**\n * 将持久化的原始状态字符串应用到 Store\n * 反序列化 → beforeRestore 钩子 → 恢复状态 → afterRestore 钩子\n */\n const applyPersistedValue = (rawState: string): void => {\n const nextState = deserialize(rawState) as Record<string, unknown>\n isRestoring = true /* 标记恢复中 */\n try {\n config.beforeRestore?.({ store }) /* 恢复前钩子 */\n restoreStoreState(store, nextState) /* 恢复状态 */\n config.afterRestore?.({ store }) /* 恢复后钩子 */\n }\n finally {\n isRestoring = false /* 恢复结束 */\n }\n }\n\n /**\n * 从存储恢复 Store 状态\n * 检查过期 → 反序列化 → 应用到 Store\n */\n const restoreState = (): void => {\n try {\n const stored = storage.getItem(key)\n if (!stored) {\n return /* 无持久化数据 */\n }\n\n const storageItem = JSON.parse(stored) as PersistedStorageItem\n /* 检查是否过期 */\n if (storageItem.expiresAt && Date.now() > storageItem.expiresAt) {\n storage.removeItem(key) /* 过期则清除 */\n return\n }\n\n /* 应用持久化值 */\n applyPersistedValue(storageItem.state)\n\n if (debug) {\n console.info(`[PersistPlugin] 已恢复状态 (${store.$id})`)\n }\n }\n catch (error) {\n console.error(`[PersistPlugin] 恢复状态失败 (${store.$id}):`, error)\n }\n }\n\n /**\n * 将当前 Store 状态持久化到存储\n * 过滤状态 → 序列化 → 包装为存储项 → 写入存储\n */\n const persistState = (): void => {\n /* 正在恢复时跳过保存,避免循环 */\n if (isRestoring) {\n return\n }\n\n try {\n /* 根据 include/exclude 路径过滤状态 */\n const filteredState = filterState(\n store.$state as Record<string, unknown>,\n config.paths,\n config.excludePaths,\n )\n\n /* 构建存储项 */\n const payload: PersistedStorageItem = {\n state: serializer(filteredState), /* 序列化过滤后的状态 */\n timestamp: Date.now(), /* 保存时间 */\n expiresAt: ttl ? Date.now() + ttl : undefined, /* 过期时间 */\n version: 1, /* 数据版本号 */\n }\n\n /* 写入存储 */\n storage.setItem(key, JSON.stringify(payload))\n\n if (debug) {\n console.info(`[PersistPlugin] 已保存状态 (${store.$id})`)\n }\n }\n catch (error) {\n console.error(`[PersistPlugin] 保存状态失败 (${store.$id}):`, error)\n }\n }\n\n /* 创建防抖版的保存函数 */\n const saveState = debounce(persistState, config.debounce ?? debounceDelay)\n\n /* 订阅 Store 状态变化,自动触发保存 */\n const stopSubscribe = store.$subscribe(() => {\n /* 恢复中不保存 */\n if (!isRestoring) {\n saveState()\n }\n })\n\n /* ── 跨标签页同步 ── */\n let stopStorageSync = (): void => {} /* 清理函数占位 */\n if (syncTabs && typeof window !== 'undefined') {\n /* 监听 storage 事件,同步其他标签页的状态变化 */\n const handleStorage = (event: StorageEvent): void => {\n /* 只关注目标 key 且有新值 */\n if (event.key !== key || !event.newValue) {\n return\n }\n\n try {\n const storageItem = JSON.parse(event.newValue) as PersistedStorageItem\n /* 跳过过期数据 */\n if (storageItem.expiresAt && Date.now() > storageItem.expiresAt) {\n return\n }\n\n /* 应用其他标签页的状态 */\n applyPersistedValue(storageItem.state)\n }\n catch (error) {\n console.error(`[PersistPlugin] 同步状态失败 (${store.$id}):`, error)\n }\n }\n\n window.addEventListener('storage', handleStorage)\n /* 保存清理函数 */\n stopStorageSync = () => {\n window.removeEventListener('storage', handleStorage)\n }\n }\n\n /**\n * 该 Store 的清理函数\n * 取消防抖、停止订阅、移除事件监听\n */\n const cleanup = (): void => {\n saveState.cancel() /* 取消待执行的防抖保存 */\n stopSubscribe() /* 取消状态订阅 */\n stopStorageSync() /* 移除跨标签页同步监听 */\n cleanupMap.delete(key) /* 从清理映射中移除 */\n }\n\n /* 注册清理函数 */\n cleanupMap.set(key, cleanup)\n /* 为 Store 的 $dispose 打补丁,确保销毁时自动清理 */\n patchPersistDispose(store as DisposableStore, cleanup)\n /* 立即尝试从存储恢复状态 */\n restoreState()\n }\n\n return {\n plugin, /* Pinia 插件函数 */\n dispose, /* 全局清理函数 */\n }\n}\n\n/**\n * 创建基础版持久化插件\n * @param options - 插件配置\n * @returns Pinia 插件函数\n */\nexport function createPersistPlugin(options: PersistPluginOptions = {}): PiniaPlugin {\n return createPersistPluginController(options).plugin\n}\n\n/**\n * 创建增强版持久化插件\n * 与基础版功能相同,类型签名保留扩展空间\n * @param options - 插件配置\n * @returns Pinia 插件函数\n */\nexport function createEnhancedPersistPlugin(\n options: EnhancedPersistPluginOptions = {},\n): PiniaPlugin {\n return createPersistPluginController(options).plugin\n}\n\n/**\n * 全局清理所有活跃的持久化控制器\n * 通常在应用卸载或测试中调用\n */\nexport function cleanupEnhancedPersist(): void {\n Array.from(activePersistControllers).forEach(dispose => dispose())\n}\n","// 从 pinia 导入 Pinia 类型\nimport type { Pinia } from 'pinia'\n// 导入增强持久化插件选项类型\nimport type { EnhancedPersistPluginOptions } from '../plugins/pinia-plugin'\n\n/**\n * store 运行时配置选项接口\n * 用于配置 store 运行时的行为\n */\nexport interface StoreRuntimeOptions {\n // 可选的 Pinia 实例,如果不提供则自动创建\n pinia?: Pinia\n // 是否启用持久化功能\n persist?: boolean\n // 持久化插件的详细配置选项\n persistOptions?: EnhancedPersistPluginOptions\n // 是否将 store API 注册到 Vue 全局属性\n globalProperties?: boolean\n // 是否通过 provide/inject 提供 store 实例\n provide?: boolean\n // 是否开启调试模式\n debug?: boolean\n}\n\n/**\n * store Vue 插件选项接口\n * 继承自 StoreRuntimeOptions,当前无额外扩展\n */\nexport interface StoreVuePluginOptions extends StoreRuntimeOptions {}\n\n/**\n * 解析后的 store 运行时选项接口\n * 所有字段均已填充默认值,不再包含可选标记\n */\nexport interface ResolvedStoreRuntimeOptions {\n // Pinia 实例\n pinia?: Pinia\n // 是否启用持久化\n persist: boolean\n // 持久化插件配置\n persistOptions: EnhancedPersistPluginOptions\n // 是否注册全局属性\n globalProperties: boolean\n // 是否通过 provide 提供\n provide: boolean\n // 是否开启调试\n debug: boolean\n}\n\n/**\n * 将用户传入的 store 运行时选项规范化为完整配置\n * 为每个可选字段填充默认值\n * @param options - 用户传入的配置选项\n * @returns 规范化后的完整配置对象\n */\nexport function normalizeStoreRuntimeOptions(\n options: StoreRuntimeOptions = {},\n): ResolvedStoreRuntimeOptions {\n return {\n pinia: options.pinia, // 透传 Pinia 实例\n persist: options.persist ?? false, // 默认不启用持久化\n persistOptions: options.persistOptions ?? {}, // 默认空持久化配置\n globalProperties: options.globalProperties ?? true, // 默认注册全局属性\n provide: options.provide ?? true, // 默认通过 provide 提供\n debug: options.debug ?? false, // 默认关闭调试\n }\n}\n","/* ============================================================\n * store-registry.ts\n * Store 注册表模块\n * 管理所有活跃的 Pinia Store 实例,提供注册/注销/查询/重置能力\n * 通过 patch $dispose 方法实现 Store 销毁时自动注销\n * ============================================================ */\n\n/* 从 Pinia 导入类型 */\nimport type { Pinia, PiniaPluginContext, StoreGeneric } from 'pinia'\n/* 导入全局 Store API 类型 */\nimport type { GlobalStoreAPI } from '../types'\n\n/* 用于标记 store 的 $dispose 是否已被 patch 的 Symbol 键 */\nconst STORE_DISPOSE_PATCH_KEY = Symbol('ldesign-store-registry-dispose')\n\n/* 可被 patch $dispose 的 Store 扩展类型 */\ntype DisposableStore = StoreGeneric & {\n [STORE_DISPOSE_PATCH_KEY]?: () => void /* 恢复原始 $dispose 的函数 */\n}\n\n/* 扩展 Pinia 类型,访问内部的 _s(Store 实例映射) */\ntype PiniaWithStores = Pinia & {\n _s?: Map<string, StoreGeneric>\n}\n\n/**\n * Store 注册表接口\n * 提供对所有活跃 Store 的集中管理能力\n * @property stores - Store 实例映射(storeId → Store)\n * @property register - 注册一个 Store\n * @property unregister - 注销一个 Store\n * @property hasStore - 检查 Store 是否已注册\n * @property getStore - 获取指定 ID 的 Store\n * @property getStoreIds - 获取所有已注册的 Store ID\n * @property resetAll - 重置所有 Store 到初始状态\n * @property clear - 清空注册表(不重置 Store)\n * @property registerExistingStores - 从 Pinia 实例中批量注册已有的 Store\n * @property createStoreAPI - 创建全局 Store API 对象\n */\nexport interface StoreRegistry {\n readonly stores: Map<string, StoreGeneric>\n register: (store: StoreGeneric) => void\n unregister: (storeId: string) => void\n hasStore: (storeId: string) => boolean\n getStore: <T = unknown>(storeId: string) => T | undefined\n getStoreIds: () => string[]\n resetAll: () => void\n clear: () => void\n registerExistingStores: (pinia: Pinia) => void\n createStoreAPI: (pinia: Pinia) => GlobalStoreAPI\n}\n\n/**\n * 为 Store 的 $dispose 方法打补丁\n * 在 Store 被销毁时自动从注册表中注销\n * 确保注册表中的 Store 引用不会过期\n *\n * @param store - 目标 Store\n * @param unregister - 注销回调函数\n */\nfunction patchStoreDispose(store: DisposableStore, unregister: (storeId: string) => void): void {\n /* 如果之前已有补丁,先恢复原始方法 */\n const restorePatchedDispose = store[STORE_DISPOSE_PATCH_KEY]\n if (restorePatchedDispose) {\n restorePatchedDispose()\n }\n\n /* 保存原始 $dispose 方法 */\n const originalDispose = store.$dispose\n /* 替换为带注销逻辑的版本 */\n store.$dispose = function patchedDispose() {\n unregister(store.$id) /* 从注册表注销 */\n return originalDispose.call(this) /* 调用原始 dispose */\n }\n\n /* 保存恢复函数,供后续补丁使用 */\n store[STORE_DISPOSE_PATCH_KEY] = () => {\n store.$dispose = originalDispose\n delete store[STORE_DISPOSE_PATCH_KEY]\n }\n}\n\n/**\n * 创建 Store 注册表实例\n * @returns StoreRegistry 注册表对象\n */\nexport function createStoreRegistry(): StoreRegistry {\n /* Store 实例映射(storeId → Store) */\n const stores = new Map<string, StoreGeneric>()\n\n /**\n * 注销指定 ID 的 Store\n * @param storeId - Store ID\n */\n const unregister = (storeId: string): void => {\n stores.delete(storeId)\n }\n\n /**\n * 注册一个 Store\n * 同时为 $dispose 打补丁,确保销毁时自动注销\n * @param store - 要注册的 Store 实例\n */\n const register = (store: StoreGeneric): void => {\n stores.set(store.$id, store)\n patchStoreDispose(store as DisposableStore, unregister)\n }\n\n /**\n * 检查 Store 是否已注册\n * @param storeId - Store ID\n * @returns 是否已注册\n */\n const hasStore = (storeId: string): boolean => {\n return stores.has(storeId)\n }\n\n /**\n * 获取指定 ID 的 Store\n * @param storeId - Store ID\n * @returns Store 实例或 undefined\n */\n const getStore = <T = unknown>(storeId: string): T | undefined => {\n return stores.get(storeId) as T | undefined\n }\n\n /**\n * 获取所有已注册的 Store ID 列表\n * @returns Store ID 数组\n */\n const getStoreIds = (): string[] => {\n return Array.from(stores.keys())\n }\n\n /**\n * 重置所有 Store 到初始状态\n * 调用每个 Store 的 $reset() 方法\n * 注意:Setup stores 可能不支持 $reset\n */\n const resetAll = (): void => {\n stores.forEach((store) => {\n /* 检查是否有 $reset 方法 */\n if (typeof store.$reset !== 'function') {\n return\n }\n\n try {\n store.$reset()\n }\n catch {\n /* Setup stores 可能暴露了 $reset 但实际拒绝直接重置 */\n }\n })\n }\n\n /**\n * 清空注册表\n * 只清除映射,不重置 Store\n */\n const clear = (): void => {\n stores.clear()\n }\n\n /**\n * 从 Pinia 实例中批量注册已有的 Store\n * 访问 Pinia 内部的 _s 映射(所有已创建的 Store)\n * @param pinia - Pinia 实例\n */\n const registerExistingStores = (pinia: Pinia): void => {\n const typedPinia = pinia as PiniaWithStores\n typedPinia._s?.forEach(store => register(store))\n }\n\n /**\n * 创建全局 Store API 对象\n * 提供只读的 Store 查询和管理能力\n * @param pinia - Pinia 实例\n * @returns GlobalStoreAPI 对象\n */\n const createStoreAPI = (pinia: Pinia): GlobalStoreAPI => {\n return {\n pinia, /* Pinia 实例引用 */\n getStore, /* 获取 Store */\n hasStore, /* 检查 Store 是否存在 */\n getStoreIds, /* 获取所有 Store ID */\n resetAll, /* 重置所有 Store */\n }\n }\n\n /* 返回完整的注册表对象 */\n return {\n stores,\n register,\n unregister,\n hasStore,\n getStore,\n getStoreIds,\n resetAll,\n clear,\n registerExistingStores,\n createStoreAPI,\n }\n}\n\n/* ── 全局注册表单例 ── */\n\n/* 全局注册表实例(模块级单例) */\nlet globalRegistry = createStoreRegistry()\n\n/**\n * 设置全局注册表\n * 传 null 则创建新的空注册表\n * @param registry - 新的注册表或 null\n */\nexport function setGlobalStoreRegistry(registry: StoreRegistry | null): void {\n globalRegistry = registry ?? createStoreRegistry()\n}\n\n/**\n * 获取全局 Store 映射(兼容旧接口)\n * @returns Store 映射(类型擦除版)\n */\nexport function getStoreRegistry(): Map<string, unknown> {\n return globalRegistry.stores as Map<string, unknown>\n}\n\n/**\n * 创建注册表方法集合\n * 从注册表中提取常用的查询方法\n * @param registry - 注册表实例,默认使用全局注册表\n * @returns 方法集合对象\n */\nexport function createRegistryMethods(registry: StoreRegistry = globalRegistry) {\n return {\n getStore: registry.getStore,\n hasStore: registry.hasStore,\n getStoreIds: registry.getStoreIds,\n resetAll: registry.resetAll,\n }\n}\n\n/**\n * 创建注册表 Pinia 插件\n * 当 Store 被创建/使用时自动注册到注册表\n * @param registry - 注册表实例,默认使用全局注册表\n * @returns Pinia 插件函数\n */\nexport function createStoreRegistryPlugin(registry: StoreRegistry = globalRegistry) {\n return ({ store }: PiniaPluginContext): void => {\n registry.register(store)\n }\n}\n","/* ============================================================\n * store-runtime.ts\n * Store 运行时模块\n * 负责创建和管理 Store 运行时实例,包括 Pinia 创建、\n * 注册表初始化、持久化插件配置和生命周期管理\n * ============================================================ */\n\n/* 从 Pinia 导入类型 */\nimport type { Pinia, StoreGeneric } from 'pinia'\n/* 全局 Store API 类型 */\nimport type { GlobalStoreAPI } from '../types'\n/* 运行时选项类型 */\nimport type {\n ResolvedStoreRuntimeOptions, /* 解析后的运行时选项 */\n StoreRuntimeOptions, /* 原始运行时选项 */\n} from './store-plugin-options'\n/* 注册表类型 */\nimport type { StoreRegistry } from './store-registry'\n/* 从 Pinia 导入创建实例函数 */\nimport { createPinia } from 'pinia'\n/* 导入持久化插件控制器 */\nimport { createPersistPluginController } from '../plugins/pinia-plugin'\n/* 导入选项规范化函数 */\nimport {\n normalizeStoreRuntimeOptions,\n} from './store-plugin-options'\n/* 导入注册表相关函数 */\nimport {\n createStoreRegistry,\n createStoreRegistryPlugin,\n setGlobalStoreRegistry,\n} from './store-registry'\n\n/* 用于标记 Pinia 实例上已绑定的运行时的 Symbol 键 */\nconst STORE_RUNTIME_KEY = Symbol('ldesign-store-runtime')\n\n/* 扩展 Pinia 类型,添加运行时和 Store 映射引用 */\ntype PiniaWithRuntime = Pinia & {\n _s?: Map<string, StoreGeneric> /* Pinia 内部的 Store 实例映射 */\n [STORE_RUNTIME_KEY]?: StoreRuntime /* 绑定的运行时 */\n}\n\n/**\n * Store 运行时接口\n * 封装了 Pinia、注册表、API 和生命周期管理\n * @property pinia - Pinia 实例\n * @property registry - Store 注册表\n * @property storeAPI - 全局 Store API\n * @property options - 解析后的运行时选项\n * @property dispose - 清理函数,释放所有资源\n */\nexport interface StoreRuntime {\n pinia: Pinia\n registry: StoreRegistry\n storeAPI: GlobalStoreAPI\n options: ResolvedStoreRuntimeOptions\n dispose: () => void\n}\n\n/* 全局运行时实例(模块级单例) */\nlet globalStoreRuntime: StoreRuntime | null = null\n\n/**\n * 获取全局 Store API\n * @returns 全局 Store API 或 null(运行时未初始化时)\n */\nexport function getGlobalStoreAPI(): GlobalStoreAPI | null {\n return globalStoreRuntime?.storeAPI ?? null\n}\n\n/**\n * 设置全局运行时\n * 同步更新全局注册表引用\n * @param runtime - 新的运行时实例或 null\n */\nexport function setGlobalStoreRuntime(runtime: StoreRuntime | null): void {\n globalStoreRuntime = runtime\n /* 同步更新全局注册表 */\n setGlobalStoreRegistry(runtime?.registry ?? null)\n}\n\n/**\n * 创建 Store 运行时实例\n * 这是 Store 系统的核心工厂函数,负责:\n * 1. 规范化配置选项\n * 2. 创建或复用 Pinia 实例\n * 3. 创建 Store 注册表\n * 4. 配置持久化插件\n * 5. 注册 Pinia 插件\n * 6. 组装运行时对象\n *\n * @param options - 运行时配置选项\n * @returns StoreRuntime 实例\n */\nexport function createStoreRuntime(\n options: StoreRuntimeOptions = {},\n): StoreRuntime {\n /* 规范化配置选项(填充默认值) */\n const resolvedOptions = normalizeStoreRuntimeOptions(options)\n /* 创建或复用 Pinia 实例,扩展为带运行时标记的类型 */\n const pinia = (resolvedOptions.pinia ?? createPinia()) as PiniaWithRuntime\n\n /* 如果 Pinia 实例上已有运行时,直接复用(防止重复创建) */\n if (pinia[STORE_RUNTIME_KEY]) {\n return pinia[STORE_RUNTIME_KEY]\n }\n\n /* 创建 Store 注册表 */\n const registry = createStoreRegistry()\n\n /* 如果启用了持久化,创建持久化插件控制器 */\n const persistController = resolvedOptions.persist\n ? createPersistPluginController({\n ...resolvedOptions.persistOptions, /* 透传持久化选项 */\n debug: resolvedOptions.debug || resolvedOptions.persistOptions.debug, /* 调试模式 */\n })\n : null\n\n /* 注册注册表插件(自动注册新创建的 Store) */\n pinia.use(createStoreRegistryPlugin(registry))\n /* 如果有持久化插件,也注册到 Pinia */\n persistController && pinia.use(persistController.plugin)\n /* 批量注册 Pinia 中已有的 Store */\n registry.registerExistingStores(pinia)\n\n /* 组装运行时对象 */\n const runtime: StoreRuntime = {\n pinia, /* Pinia 实例 */\n registry, /* Store 注册表 */\n storeAPI: registry.createStoreAPI(pinia), /* 全局 Store API */\n options: resolvedOptions, /* 解析后的选项 */\n\n /**\n * 清理函数\n * 释放所有资源:清除运行时标记、持久化插件、注册表\n */\n dispose: () => {\n /* 清除 Pinia 上的运行时标记 */\n if (pinia[STORE_RUNTIME_KEY] === runtime) {\n delete pinia[STORE_RUNTIME_KEY]\n }\n\n /* 销毁持久化插件(取消订阅、移除事件监听等) */\n persistController?.dispose()\n /* 清空注册表 */\n registry.clear()\n\n /* 如果当前是全局运行时,也清除全局引用 */\n if (globalStoreRuntime === runtime) {\n setGlobalStoreRuntime(null)\n }\n },\n }\n\n /* 在 Pinia 实例上保存运行时引用(防止重复创建) */\n pinia[STORE_RUNTIME_KEY] = runtime\n return runtime\n}\n","/* ============================================================\n * install-store-app.ts\n * Store 运行时安装到 Vue App 的模块\n * 负责将 StoreRuntime 安装到 Vue 应用实例,管理 Pinia 注入、\n * provide/globalProperties 注入和 unmount 生命周期清理\n * ============================================================ */\n\n/* Vue App 类型 */\nimport type { App } from 'vue'\n/* Store 运行时类型 */\nimport type { StoreRuntime } from './store-runtime'\n/* 从注入键模块导入 Symbol 常量 */\nimport {\n STORE_API_SYMBOL, /* Store API 的注入键 */\n STORE_PINIA_SYMBOL, /* Pinia 实例的注入键 */\n STORE_RUNTIME_SYMBOL, /* 运行时的注入键 */\n} from '../context/injection-keys'\n/* 导入全局运行时设置函数 */\nimport { setGlobalStoreRuntime } from './store-runtime'\n\n/* 用于标记 App 上已安装的运行时的 Symbol 键 */\nconst STORE_APP_RUNTIME_KEY = Symbol('ldesign-store-app-runtime')\n\n/* 扩展 Vue App 类型,添加运行时引用 */\ntype StoreApp = App & {\n [STORE_APP_RUNTIME_KEY]?: StoreRuntime\n}\n\n/**\n * 检查 Pinia 是否已安装到 Vue App\n * 通过比较 app.config.globalProperties.$pinia 与 runtime 的 pinia 实例\n *\n * @param app - Vue 应用实例\n * @param runtime - Store 运行时\n * @returns Pinia 是否已安装\n */\nfunction isPiniaInstalled(app: App, runtime: StoreRuntime): boolean {\n return app.config.globalProperties.$pinia === runtime.pinia\n}\n\n/**\n * 为 Vue App 的 unmount 方法打补丁\n * 在应用卸载时自动清理 Store 运行时(dispose)\n * 确保资源不会泄漏\n *\n * @param app - Vue 应用实例\n * @param runtime - Store 运行时\n */\nfunction patchUnmount(app: StoreApp, runtime: StoreRuntime): void {\n /* 保存原始 unmount 方法 */\n const originalUnmount = app.unmount\n\n /* 替换为带清理逻辑的版本 */\n app.unmount = function patchedUnmount() {\n /* 检查当前运行时是否仍是这个 runtime(防止被后续安装覆盖) */\n const activeRuntime = app[STORE_APP_RUNTIME_KEY]\n if (activeRuntime === runtime) {\n /* 清除 App 上的运行时引用 */\n delete app[STORE_APP_RUNTIME_KEY]\n /* 执行运行时的 dispose(清理持久化、注册表等) */\n runtime.dispose()\n }\n\n /* 调用原始 unmount 方法 */\n return originalUnmount.call(this)\n }\n}\n\n/**\n * 获取 Vue App 上已安装的 Store 运行时\n * @param app - Vue 应用实例\n * @returns 已安装的运行时或 undefined\n */\nexport function getInstalledStoreRuntime(app: App): StoreRuntime | undefined {\n return (app as StoreApp)[STORE_APP_RUNTIME_KEY]\n}\n\n/**\n * 将 Store 运行时安装到 Vue App\n * 这是 Vue 适配层的核心安装入口,负责:\n * 1. 安装 Pinia 插件\n * 2. 通过 provide 注入 Store 能力\n * 3. 挂载到 globalProperties\n * 4. 打 unmount 补丁进行生命周期管理\n * 5. 设置全局运行时引用\n *\n * @param app - Vue 应用实例\n * @param runtime - Store 运行时实例\n * @returns 安装的运行时(如果已安装则返回已有的)\n */\nexport function installStoreRuntime(\n app: App,\n runtime: StoreRuntime,\n): StoreRuntime {\n const storeApp = app as StoreApp\n /* 检查是否已安装(幂等性) */\n const installedRuntime = storeApp[STORE_APP_RUNTIME_KEY]\n\n if (installedRuntime) {\n /* 已安装则设置全局引用并返回已有的运行时 */\n setGlobalStoreRuntime(installedRuntime)\n return installedRuntime\n }\n\n /* 如果 Pinia 尚未安装,使用 app.use() 安装 */\n if (!isPiniaInstalled(app, runtime)) {\n app.use(runtime.pinia)\n }\n\n /* 在 App 上保存运行时引用(标记已安装) */\n storeApp[STORE_APP_RUNTIME_KEY] = runtime\n\n /* 如果配置了 provide 注入,通过 Vue 的 provide/inject 注入 Store 能力 */\n if (runtime.options.provide) {\n app.provide(STORE_RUNTIME_SYMBOL, runtime) /* 注入运行时 */\n app.provide(STORE_API_SYMBOL, runtime.storeAPI) /* 注入 Store API */\n app.provide(STORE_PINIA_SYMBOL, runtime.pinia) /* 注入 Pinia 实例 */\n app.provide('store', runtime.storeAPI) /* 简写键 'store' */\n app.provide('pinia', runtime.pinia) /* 简写键 'pinia' */\n }\n\n /* 如果配置了 globalProperties,挂载到全局属性上 */\n if (runtime.options.globalProperties) {\n app.config.globalProperties.$store = runtime.storeAPI\n }\n\n /* 打 unmount 补丁,确保卸载时自动清理 */\n patchUnmount(storeApp, runtime)\n /* 设置为全局运行时引用 */\n setGlobalStoreRuntime(runtime)\n\n return runtime\n}\n","/* ============================================================\n * engine-plugin.ts\n * Store Engine 插件(Vue 适配层)\n * 将 core 层的 createStoreEngineBridge 包装为 Vue 友好的插件接口\n * 负责创建 bridge、管理运行时生命周期、连接 Vue App\n * ============================================================ */\n\n/* 从 core/engine 导入 bridge 相关类型 */\nimport type {\n StoreEngineBridge, /* 引擎桥接实例接口 */\n StoreEngineLike, /* 引擎抽象接口 */\n StoreEnginePluginContextLike, /* 插件上下文接口 */\n} from '@composy/store-core/engine'\n/* Vue App 类型 */\nimport type { App } from 'vue'\n/* 本地 store 运行时类型 */\nimport type { StoreRuntime } from '../runtime/store-runtime'\n/* 全局 Store API 和插件选项类型 */\nimport type { GlobalStoreAPI, StoreEnginePluginOptions } from '../types'\n/* 从 core/engine 导入 bridge 创建函数 */\nimport { createStoreEngineBridge } from '@composy/store-core/engine'\n/* 导入 Vue App 运行时安装相关函数 */\nimport { getInstalledStoreRuntime, installStoreRuntime } from '../runtime/install-store-app'\n/* 导入运行时创建函数和全局 API 获取函数 */\nimport { createStoreRuntime, getGlobalStoreAPI as getActiveStoreAPI } from '../runtime/store-runtime'\n\n/**\n * Store Engine 插件接口\n * 定义了插件的元信息和生命周期方法\n * @property dependencies - 依赖的其他插件名称列表\n * @property name - 插件名称\n * @property version - 插件版本\n * @property install - 安装到引擎上下文(异步)\n * @property installVue - 安装到 Vue App\n * @property uninstall - 从引擎上下文卸载(异步)\n */\nexport interface StoreEnginePlugin {\n dependencies: string[]\n name: string\n version: string\n install: (context: StoreEnginePluginContextLike) => Promise<void>\n installVue: (app: App) => void\n uninstall: (context?: StoreEnginePluginContextLike) => Promise<void>\n}\n\n/**\n * 类型守卫:判断值是否为 Vue App 实例\n * 通过检查是否有 use 方法来识别\n */\nfunction isVueApp(value: unknown): value is App {\n return !!value && typeof (value as App).use === 'function'\n}\n\n/**\n * 从各种可能的来源解析 Vue App 实例\n * 按优先级尝试:payload.app → payload.framework.app → engine.getApp() → context.framework.app\n *\n * @param engine - Store 引擎实例\n * @param context - 插件上下文\n * @param payload - 安装时传入的额外数据\n * @returns 解析到的 Vue App 或 undefined\n */\nfunction resolveVueApp(\n engine: StoreEngineLike,\n context: StoreEnginePluginContextLike,\n payload?: unknown,\n): App | undefined {\n /* 将 payload 宽松转为可能包含 app 字段的对象 */\n const appPayload = payload as { app?: unknown; framework?: { app?: unknown } } | undefined\n /* 按优先级尝试多个来源获取 app */\n const app = appPayload?.app\n ?? appPayload?.framework?.app\n ?? engine.getApp?.()\n ?? context.framework?.app\n\n /* 使用类型守卫验证是否为 Vue App */\n return isVueApp(app) ? app : undefined\n}\n\n/**\n * 获取当前活跃的全局 Store API\n * @returns 全局 Store API 或 null(未初始化时)\n */\nexport function getGlobalStoreAPI(): GlobalStoreAPI | null {\n return getActiveStoreAPI()\n}\n\n/**\n * 创建 Store Engine 插件工厂函数\n * 这是 Vue 适配层的核心入口,将 core 层的 bridge 包装为 Vue 友好的插件\n *\n * @param options - 插件配置选项\n * @returns StoreEnginePlugin 插件实例\n */\nexport function createStoreEnginePlugin(options: StoreEnginePluginOptions = {}): StoreEnginePlugin {\n /* 解构配置选项,设置默认值 */\n const {\n debug = false, /* 是否开启调试日志 */\n dependencies = [], /* 依赖的插件列表 */\n name = 'store', /* 插件名称 */\n version = '1.0.0', /* 插件版本 */\n } = options\n\n /* 延迟创建的 bridge 实例引用 */\n let bridge: StoreEngineBridge | null = null\n\n /**\n * 创建 bridge 的工厂函数\n * 内部调用 core 层的 createStoreEngineBridge,并传入 Vue 特定的实现\n */\n const createBridge = (): StoreEngineBridge => {\n const activeBridge = createStoreEngineBridge<StoreRuntime>({\n name, /* 插件名称 */\n version, /* 插件版本 */\n debug, /* 调试模式 */\n\n /* 创建运行时:传入所有 Vue 相关配置 */\n createRuntime: () => createStoreRuntime({\n pinia: options.pinia, /* 可选的外部 Pinia 实例 */\n persist: options.persist, /* 是否启用持久化 */\n persistOptions: options.persistOptions, /* 持久化选项 */\n globalProperties: options.globalProperties, /* 是否挂载到全局属性 */\n provide: options.provide, /* 是否通过 provide 注入 */\n debug,\n }),\n\n /* 将运行时安装到 Vue App */\n attachToApp: (app, runtime) => installStoreRuntime(app as App, runtime),\n\n /* 从 Vue App 获取已安装的运行时 */\n getRuntimeFromApp: app => getInstalledStoreRuntime(app as App),\n\n /* Vue App 类型守卫 */\n isApp: isVueApp,\n\n /* Vue App 解析函数 */\n resolveApp: resolveVueApp,\n })\n\n bridge = activeBridge\n return activeBridge\n }\n\n /**\n * 获取 bridge 实例(懒创建)\n * 首次调用时自动创建\n */\n const getBridge = (): StoreEngineBridge => {\n return bridge ?? createBridge()\n }\n\n /* ── 返回插件对象 ── */\n return {\n name, /* 插件名称 */\n version, /* 插件版本 */\n dependencies, /* 插件依赖 */\n\n /**\n * 安装到引擎上下文(异步)\n * 通过 bridge 将 store 能力注册到引擎\n */\n async install(context: StoreEnginePluginContextLike): Promise<void> {\n await getBridge().install(context)\n },\n\n /**\n * 安装到 Vue App\n * 将 store 运行时连接到 Vue 应用实例\n */\n installVue(app: App): void {\n getBridge().attachToApp(app)\n },\n\n /**\n * 从引擎上下文卸载(异步)\n * 清理 bridge 引用\n */\n async uninstall(context?: StoreEnginePluginContextLike): Promise<void> {\n await getBridge().uninstall(context)\n bridge = null /* 释放 bridge 引用,允许垃圾回收 */\n },\n }\n}\n\n/* 默认导出工厂函数 */\nexport default createStoreEnginePlugin\n","// 从 vue 导入 App 和 Plugin 类型\nimport type { App, Plugin } from 'vue'\n// 导入 store Vue 插件选项类型\nimport type { StoreVuePluginOptions } from '../runtime/store-plugin-options'\n// 导入安装 store 运行时的函数\nimport { installStoreRuntime } from '../runtime/install-store-app'\n// 导入创建 store 运行时的函数\nimport { createStoreRuntime } from '../runtime/store-runtime'\n\n// 重新导出 store Vue 插件选项类型\nexport type { StoreVuePluginOptions } from '../runtime/store-plugin-options'\n\n/**\n * 创建 store Vue 插件\n * @param options - store Vue 插件配置选项\n * @returns 返回 Vue Plugin 对象,可通过 app.use() 安装\n */\nexport function createStoreVuePlugin(options: StoreVuePluginOptions = {}): Plugin {\n return {\n // 安装插件到 Vue 应用\n install(app: App): void {\n // 创建 store 运行时实例\n const runtime = createStoreRuntime(options)\n // 将运行时安装到 Vue 应用,如果已安装则返回已有的运行时\n const installedRuntime = installStoreRuntime(app, runtime)\n\n // 如果安装的运行时不是新创建的,则销毁新建的运行时以避免内存泄漏\n if (installedRuntime !== runtime) {\n runtime.dispose()\n }\n\n // 如果开启了调试模式,输出安装成功日志\n if (installedRuntime.options.debug) {\n console.info('[StoreVuePlugin] installed')\n }\n },\n }\n}\n","/**\n * @composy/store-vue - Vue 3 状态管理适配器\n *\n * 基于 Pinia 的增强版状态管理解决方案\n *\n * 主要功能:\n * - Pinia 集成与增强\n * - 状态持久化\n * - Engine 插件集成\n * - 丰富的 Composables\n *\n * @module @composy/store-vue\n */\n\n// === Composables ===\nexport {\n createStoreGetters,\n createStoreState,\n // Setup Store 助手\n defineSetupStore,\n email,\n maxLength,\n minLength,\n numberRange,\n pattern,\n // 验证规则工具\n required,\n useComputedStore,\n useFormStore,\n useOnAction,\n useOptimistic,\n usePersist,\n usePersistedRef,\n usePersistedRefWithTTL,\n usePersistedState,\n useSharedState,\n useSimpleStore,\n useStore,\n useStoreDevtools,\n useStoreSnapshot,\n useSubscribe,\n useWatch,\n} from './composables'\n\nexport type {\n ActionContext,\n EventLogEntry,\n FormFieldRules,\n FormFieldState,\n FormValidator,\n OptimisticEntry,\n PersistedStateOptions,\n PersistedStateReturn,\n StateDiff,\n StateSnapshot,\n StoreInfo,\n UseFormStoreOptions,\n UseFormStoreReturn,\n UseOptimisticReturn,\n UseSharedStateOptions,\n UseSharedStateReturn,\n UseStoreDevtoolsReturn,\n UseStoreSnapshotOptions,\n UseStoreSnapshotReturn,\n} from './composables'\n\n// === 插件 ===\nexport {\n cleanupEnhancedPersist,\n createEnhancedPersistPlugin,\n createPersistPlugin,\n createStoreEnginePlugin,\n createStoreRegistryPlugin,\n createStoreVuePlugin,\n getGlobalStoreAPI,\n getStoreRegistry,\n} from './plugins'\n\nexport type {\n EnhancedPersistOptions,\n EnhancedPersistPluginOptions,\n PersistPluginOptions,\n StoreVuePluginOptions,\n} from './plugins'\n\n// === 类型 ===\nexport type {\n GlobalStoreAPI,\n MutationInfo,\n PersistOptions,\n StoreEnginePluginOptions,\n StoreOptions,\n StoreProviderProps,\n SubscribeOptions,\n UsePersistOptions,\n UsePersistReturn,\n UseStoreReturn,\n} from './types'\n\n// === 从 @composy/store-core 重新导出常用功能 ===\nexport {\n // 核心\n batch,\n // 工具\n combineSelectors,\n // 插件\n composePlugins,\n createAsyncAction,\n createBatchManager,\n createEventBus,\n // 中间件\n createLoggerMiddleware,\n createMiddlewareManager,\n createPluginFactory,\n createPubSub,\n createPubSubWithValue,\n createSelector,\n createTimeTravelMiddleware,\n createValidationMiddleware,\n deepClone,\n deepMerge,\n defineStorePlugin,\n globalEventBus,\n} from '@composy/store-core'\n\n// === 从 Pinia 重新导出 ===\nexport {\n createPinia,\n defineStore,\n getActivePinia,\n mapActions,\n mapGetters,\n mapState,\n mapStores,\n mapWritableState,\n setActivePinia,\n storeToRefs,\n} from 'pinia'\n\nexport type {\n Pinia,\n PiniaPlugin,\n PiniaPluginContext,\n StateTree,\n Store,\n StoreDefinition,\n StoreGeneric,\n StoreGetters,\n StoreState,\n} from 'pinia'\n\n// === 版本信息 ===\nexport const version = '1.0.0'\nexport const name = '@composy/store-vue'\n\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/composables/defineSetupStore.ts","../src/composables/useFormStore.ts","../src/composables/useOptimistic.ts","../src/composables/usePersist.ts","../src/composables/usePersistedState.ts","../src/composables/useSharedState.ts","../src/composables/useStore.ts","../src/composables/useStoreDevtools.ts","../src/composables/useStoreSnapshot.ts","../src/composables/useSubscribe.ts","../src/context/injection-keys.ts","../src/plugins/pinia-plugin.ts","../src/runtime/store-plugin-options.ts","../src/runtime/store-registry.ts","../src/runtime/store-runtime.ts","../src/runtime/install-store-app.ts","../src/plugins/engine-plugin.ts","../src/plugins/vue-plugin.ts","../src/index.ts"],"sourcesContent":["/**\r\n * defineSetupStore - Setup 风格 Store 定义助手\r\n *\r\n * 提供更简洁的 composition API 风格来定义 Pinia Store。\r\n * 本质是对 defineStore 的 setup 语法的薄封装,增加类型推断和开发体验。\r\n *\r\n * @module composables/defineSetupStore\r\n */\r\n\r\nimport type { ComputedRef, Ref } from 'vue'\r\nimport { defineStore } from 'pinia'\r\nimport { computed, ref } from 'vue'\r\n\r\n/**\r\n * defineSetupStore - 使用 Composition API 风格定义 Store\r\n *\r\n * 与 defineStore 的 setup 语法等价,但提供更清晰的语义。\r\n * 适合喜欢 Composition API 风格的开发者。\r\n *\r\n * @param id - Store 唯一标识\r\n * @param setup - Setup 函数(在函数内使用 ref、computed、普通函数来定义 state、getters、actions)\r\n * @returns Pinia Store 定义\r\n *\r\n * @example\r\n * ```typescript\r\n * import { defineSetupStore } from '@composy/store-vue'\r\n * import { ref, computed } from 'vue'\r\n *\r\n * // Setup 风格定义(推荐)\r\n * export const useCounterStore = defineSetupStore('counter', () => {\r\n * // state(使用 ref)\r\n * const count = ref(0)\r\n * const step = ref(1)\r\n *\r\n * // getters(使用 computed)\r\n * const doubleCount = computed(() => count.value * 2)\r\n * const isPositive = computed(() => count.value > 0)\r\n *\r\n * // actions(普通函数)\r\n * function increment() {\r\n * count.value += step.value\r\n * }\r\n *\r\n * function decrement() {\r\n * count.value -= step.value\r\n * }\r\n *\r\n * async function fetchCount(id: string) {\r\n * const res = await fetch(`/api/count/${id}`)\r\n * count.value = await res.json()\r\n * }\r\n *\r\n * function reset() {\r\n * count.value = 0\r\n * step.value = 1\r\n * }\r\n *\r\n * return {\r\n * count, step,\r\n * doubleCount, isPositive,\r\n * increment, decrement, fetchCount, reset,\r\n * }\r\n * })\r\n * ```\r\n */\r\nexport function defineSetupStore<T>(id: string, setup: () => T) {\r\n return defineStore(id, setup)\r\n}\r\n\r\n/**\r\n * createStoreState - 创建带有类型推断的 Store State\r\n *\r\n * 工具函数,帮助在 setup store 中快速创建多个 ref 状态。\r\n *\r\n * @param initial - 初始状态对象\r\n * @returns 以 ref 包装的响应式状态对象\r\n *\r\n * @example\r\n * ```typescript\r\n * const useUserStore = defineSetupStore('user', () => {\r\n * const state = createStoreState({\r\n * name: '张三',\r\n * age: 25,\r\n * email: '',\r\n * })\r\n *\r\n * // state.name 是 Ref<string>\r\n * // state.age 是 Ref<number>\r\n *\r\n * function updateName(name: string) {\r\n * state.name.value = name\r\n * }\r\n *\r\n * return { ...state, updateName }\r\n * })\r\n * ```\r\n */\r\nexport function createStoreState<T extends Record<string, unknown>>(\r\n initial: T\r\n): { [K in keyof T]: Ref<T[K]> } {\r\n const result = {} as { [K in keyof T]: Ref<T[K]> }\r\n for (const key of Object.keys(initial) as Array<keyof T>) {\r\n result[key] = ref(initial[key]) as Ref<T[typeof key]>\r\n }\r\n return result\r\n}\r\n\r\n/**\r\n * createStoreGetters - 创建带有类型推断的 Store Getters\r\n *\r\n * 工具函数,帮助在 setup store 中快速创建多个 computed getter。\r\n *\r\n * @param getters - getter 函数映射\r\n * @returns 以 computed 包装的响应式 getter 对象\r\n *\r\n * @example\r\n * ```typescript\r\n * const useUserStore = defineSetupStore('user', () => {\r\n * const name = ref('张三')\r\n * const age = ref(25)\r\n *\r\n * const getters = createStoreGetters({\r\n * displayName: () => `${name.value} (${age.value}岁)`,\r\n * isAdult: () => age.value >= 18,\r\n * })\r\n *\r\n * return { name, age, ...getters }\r\n * })\r\n * ```\r\n */\r\nexport function createStoreGetters<T extends Record<string, () => unknown>>(\r\n getters: T\r\n): { [K in keyof T]: ComputedRef<ReturnType<T[K]>> } {\r\n const result = {} as { [K in keyof T]: ComputedRef<ReturnType<T[K]>> }\r\n for (const key of Object.keys(getters) as Array<keyof T>) {\r\n result[key] = computed(getters[key]) as ComputedRef<\r\n ReturnType<T[typeof key]>\r\n >\r\n }\r\n return result\r\n}\r\n","/* ============================================================\r\n * useFormStore.ts\r\n * 表单状态管理 Composable\r\n * 提供表单值、字段状态、校验、提交、重置等完整表单生命周期管理\r\n * ============================================================ */\r\n\r\n/* 从 Vue 导入类型,用于声明响应式引用 */\r\nimport type { ComputedRef, Ref } from 'vue'\r\n/* 从 store-core 工具库导入深拷贝和深比较函数 */\r\nimport { deepClone, deepEqual } from '@composy/store-core'\r\n/* 从 Vue 导入响应式 API */\r\nimport { computed, reactive, ref, watch } from 'vue'\r\n\r\n/* ── 默认校验提示信息 ── */\r\n/* 必填项校验失败时的默认提示 */\r\nconst DEFAULT_REQUIRED_MESSAGE = '此字段为必填项'\r\n/* 正则匹配校验失败时的默认提示 */\r\nconst DEFAULT_PATTERN_MESSAGE = '格式不正确'\r\n/* 邮箱格式校验失败时的默认提示 */\r\nconst DEFAULT_EMAIL_MESSAGE = '邮箱格式不正确'\r\n/* 邮箱格式正则表达式 */\r\nconst EMAIL_PATTERN = /^[^\\s@]+@[^@][^.@]*\\.[^\\s@]+$/\r\n\r\n/* ── 类型定义 ── */\r\n\r\n/* 从泛型 T 中提取 string 类型的键名,用作字段标识 */\r\ntype FieldKey<T> = Extract<keyof T, string>\r\n\r\n/**\r\n * 表单校验器函数类型\r\n * @param value - 当前字段值\r\n * @param allValues - 表单所有字段的值(用于跨字段校验)\r\n * @returns 校验错误信息或 null(无错误),支持异步校验\r\n */\r\nexport type FormValidator<T = unknown> = (\r\n value: T,\r\n allValues: Record<string, unknown>\r\n) => string | null | Promise<string | null>\r\n\r\n/**\r\n * 字段校验规则接口\r\n * @property required - 是否必填,可传 string 自定义提示\r\n * @property rules - 自定义校验器数组,按顺序依次执行\r\n */\r\nexport interface FormFieldRules<T = unknown> {\r\n required?: boolean | string\r\n rules?: FormValidator<T>[]\r\n}\r\n\r\n/**\r\n * useFormStore 的配置选项接口\r\n * @property initialValues - 表单初始值(必须提供完整对象)\r\n * @property validationRules - 各字段的校验规则映射\r\n * @property onSubmit - 表单提交回调(校验通过后调用)\r\n * @property validateOn - 触发校验的时机:'blur' | 'change' | 'submit'\r\n */\r\nexport interface UseFormStoreOptions<T extends Record<string, unknown>> {\r\n initialValues: T\r\n validationRules?: {\r\n [K in keyof T]?: FormFieldRules<T[K]>\r\n }\r\n onSubmit?: (values: T) => void | Promise<void>\r\n validateOn?: 'blur' | 'change' | 'submit'\r\n}\r\n\r\n/**\r\n * 单个字段的状态接口\r\n * @property touched - 字段是否被触碰过(获得过焦点或提交过)\r\n * @property dirty - 字段值是否与初始值不同(被修改过)\r\n * @property error - 当前校验错误信息,null 表示无错误\r\n * @property validating - 字段是否正在异步校验中\r\n */\r\nexport interface FormFieldState {\r\n touched: boolean\r\n dirty: boolean\r\n error: string | null\r\n validating: boolean\r\n}\r\n\r\n/**\r\n * useFormStore 返回值接口\r\n * @property values - 表单当前值(reactive 对象)\r\n * @property fields - 各字段的状态映射\r\n * @property isDirty - 是否有任意字段被修改过(计算属性)\r\n * @property isValid - 是否所有字段都无校验错误(计算属性)\r\n * @property isSubmitting - 表单是否正在提交中\r\n * @property submitError - 提交失败的错误信息\r\n * @property submitCount - 累计提交次数\r\n * @property setFieldValue - 设置指定字段的值\r\n * @property setFieldTouched - 标记字段为已触碰\r\n * @property validateField - 校验单个字段(异步)\r\n * @property validateAll - 校验所有字段(异步)\r\n * @property handleSubmit - 提交表单\r\n * @property reset - 重置整个表单(可传新的初始值)\r\n * @property resetField - 重置单个字段\r\n * @property errors - 所有字段错误信息的计算属性\r\n */\r\nexport interface UseFormStoreReturn<T extends Record<string, unknown>> {\r\n values: T\r\n fields: Record<string, FormFieldState>\r\n isDirty: ComputedRef<boolean>\r\n isValid: ComputedRef<boolean>\r\n isSubmitting: Ref<boolean>\r\n submitError: Ref<string | null>\r\n submitCount: Ref<number>\r\n setFieldValue: <K extends keyof T>(field: K, value: T[K]) => void\r\n setFieldTouched: (field: keyof T) => void\r\n validateField: (field: keyof T) => Promise<string | null>\r\n validateAll: () => Promise<boolean>\r\n handleSubmit: () => Promise<void>\r\n reset: (newInitialValues?: Partial<T>) => void\r\n resetField: (field: keyof T) => void\r\n errors: ComputedRef<Record<string, string | null>>\r\n}\r\n\r\n/**\r\n * 表单状态管理 Composable\r\n * 提供完整的表单生命周期管理能力,包括值管理、字段状态追踪、校验、提交和重置\r\n *\r\n * @param options - 表单配置选项\r\n * @returns 包含表单状态和操作方法的对象\r\n */\r\nexport function useFormStore<T extends Record<string, unknown>>(\r\n options: UseFormStoreOptions<T>\r\n): UseFormStoreReturn<T> {\r\n /* 解构配置选项,设置默认值 */\r\n const {\r\n initialValues /* 表单初始值 */,\r\n validationRules = {} as NonNullable<\r\n UseFormStoreOptions<T>['validationRules']\r\n > /* 校验规则,默认空对象 */,\r\n onSubmit /* 提交回调 */,\r\n validateOn = 'blur' /* 校验时机,默认 blur 时触发 */,\r\n } = options\r\n\r\n /* 深拷贝初始值并保存,用于后续重置和脏值比较 */\r\n const storedInitialValues = deepClone(initialValues) as T\r\n /* 创建响应式表单值对象(从初始值的深拷贝开始) */\r\n const values = reactive(deepClone(storedInitialValues)) as T\r\n /* 将 values 转为通用 Record 类型,便于动态属性访问 */\r\n const valueRecord = values as Record<string, unknown>\r\n /* 将初始值也转为 Record 类型,用于脏值比较 */\r\n const initialValueRecord = storedInitialValues as Record<string, unknown>\r\n /* 获取所有字段名列表 */\r\n const fieldKeys = Object.keys(storedInitialValues) as Array<FieldKey<T>>\r\n\r\n /* 创建每个字段的响应式状态对象(touched/dirty/error/validating) */\r\n const fields = reactive(\r\n fieldKeys.reduce<Record<string, FormFieldState>>((acc, key) => {\r\n acc[key] = {\r\n touched: false /* 字段未被触碰 */,\r\n dirty: false /* 字段值未被修改 */,\r\n error: null /* 无校验错误 */,\r\n validating: false /* 未在校验中 */,\r\n }\r\n return acc\r\n }, {})\r\n )\r\n\r\n /* ── 全局状态 ── */\r\n /* 是否正在提交中 */\r\n const isSubmitting = ref(false)\r\n /* 提交错误信息 */\r\n const submitError = ref<string | null>(null)\r\n /* 累计提交次数 */\r\n const submitCount = ref(0)\r\n\r\n /* ── 计算属性 ── */\r\n /* 是否有任意字段被修改过 */\r\n const isDirty = computed(() => fieldKeys.some(key => fields[key]?.dirty))\r\n /* 是否所有字段都无错误 */\r\n const isValid = computed(() => fieldKeys.every(key => !fields[key]?.error))\r\n\r\n /* 所有字段错误信息的映射 */\r\n const errors = computed(() => {\r\n return fieldKeys.reduce<Record<string, string | null>>((acc, key) => {\r\n acc[key] = fields[key]?.error ?? null\r\n return acc\r\n }, {})\r\n })\r\n\r\n /* ── 值变化监听 ── */\r\n /* 为每个字段创建 watch,当值变化时更新脏状态并按需触发校验 */\r\n fieldKeys.forEach(key => {\r\n watch(\r\n () => valueRecord[key] /* 监听字段值 */,\r\n newValue => {\r\n const field = fields[key]\r\n if (field) {\r\n /* 比较当前值与初始值,判断是否为脏值 */\r\n field.dirty = !deepEqual(newValue, initialValueRecord[key])\r\n }\r\n\r\n /* 如果校验时机为 'change' 且字段已被触碰,则触发校验 */\r\n if (validateOn === 'change' && field?.touched) {\r\n void validateField(key)\r\n }\r\n },\r\n { deep: true } /* 深度监听,支持嵌套对象变化 */\r\n )\r\n })\r\n\r\n /* ── 操作方法 ── */\r\n\r\n /**\r\n * 设置指定字段的值\r\n * @param field - 字段名\r\n * @param value - 新值\r\n */\r\n function setFieldValue<K extends keyof T>(field: K, value: T[K]): void {\r\n valueRecord[field as string] = value\r\n }\r\n\r\n /**\r\n * 标记字段为已触碰状态\r\n * 如果校验时机为 'blur',同时触发该字段的校验\r\n * @param field - 字段名\r\n */\r\n function setFieldTouched(field: keyof T): void {\r\n const fieldState = fields[field as string]\r\n if (fieldState) {\r\n fieldState.touched = true\r\n }\r\n\r\n /* blur 时自动触发校验 */\r\n if (validateOn === 'blur') {\r\n void validateField(field)\r\n }\r\n }\r\n\r\n /**\r\n * 校验单个字段\r\n * 按顺序执行:必填校验 → 自定义规则校验\r\n * @param field - 字段名\r\n * @returns 错误信息或 null(无错误)\r\n */\r\n async function validateField(field: keyof T): Promise<string | null> {\r\n const fieldKey = field as string\r\n const fieldState = fields[fieldKey]\r\n\r\n /* 字段状态不存在则跳过 */\r\n if (!fieldState) {\r\n return null\r\n }\r\n\r\n /* 获取该字段的校验规则 */\r\n const fieldRules = validationRules[field]\r\n /* 无规则则清除错误并返回 */\r\n if (!fieldRules) {\r\n fieldState.error = null\r\n return null\r\n }\r\n\r\n /* 标记开始校验 */\r\n fieldState.validating = true\r\n\r\n try {\r\n const value = values[field]\r\n\r\n /* 第一步:必填校验 */\r\n if (fieldRules.required) {\r\n const isEmpty = value === '' || value === null || value === undefined\r\n if (isEmpty) {\r\n /* 支持自定义必填提示信息 */\r\n const message =\r\n typeof fieldRules.required === 'string'\r\n ? fieldRules.required\r\n : DEFAULT_REQUIRED_MESSAGE\r\n fieldState.error = message\r\n return message\r\n }\r\n }\r\n\r\n /* 第二步:自定义规则校验(按顺序执行,遇到第一个错误即停止) */\r\n if (fieldRules.rules) {\r\n for (const rule of fieldRules.rules) {\r\n const error = await rule(\r\n value as T[keyof T],\r\n valueRecord /* 传入所有表单值,支持跨字段校验 */\r\n )\r\n\r\n if (error) {\r\n fieldState.error = error\r\n return error\r\n }\r\n }\r\n }\r\n\r\n /* 所有校验通过,清除错误 */\r\n fieldState.error = null\r\n return null\r\n } finally {\r\n /* 无论成功失败,标记校验结束 */\r\n fieldState.validating = false\r\n }\r\n }\r\n\r\n /**\r\n * 校验所有字段\r\n * @returns 是否所有字段都校验通过\r\n */\r\n async function validateAll(): Promise<boolean> {\r\n const results = await Promise.all(fieldKeys.map(key => validateField(key)))\r\n return results.every(result => result === null)\r\n }\r\n\r\n /**\r\n * 提交流程\r\n * 1. 递增提交计数\r\n * 2. 标记所有字段为已触碰\r\n * 3. 执行全量校验\r\n * 4. 校验通过且有 onSubmit 回调时,调用提交\r\n */\r\n async function handleSubmit(): Promise<void> {\r\n submitCount.value++\r\n submitError.value = null\r\n\r\n /* 标记所有字段为已触碰 */\r\n fieldKeys.forEach(key => {\r\n const field = fields[key]\r\n if (field) {\r\n field.touched = true\r\n }\r\n })\r\n\r\n /* 执行全量校验 */\r\n const valid = await validateAll()\r\n /* 校验未通过或没有提交回调则退出 */\r\n if (!valid || !onSubmit) {\r\n return\r\n }\r\n\r\n /* 开始提交 */\r\n isSubmitting.value = true\r\n try {\r\n /* 深拷贝 values 后传给 onSubmit,避免外部修改响应式数据 */\r\n await onSubmit(deepClone(values) as T)\r\n } catch (error) {\r\n /* 捕获提交异常,保存错误信息 */\r\n submitError.value = error instanceof Error ? error.message : String(error)\r\n } finally {\r\n /* 无论成功失败,标记提交结束 */\r\n isSubmitting.value = false\r\n }\r\n }\r\n\r\n /**\r\n * 重置整个表单\r\n * 可选传入新的初始值(部分更新)\r\n * @param newInitialValues - 新的初始值(可选,会与现有初始值合并)\r\n */\r\n function reset(newInitialValues?: Partial<T>): void {\r\n /* 如果传入了新初始值,合并到存储的初始值中 */\r\n if (newInitialValues) {\r\n Object.assign(initialValueRecord, deepClone(newInitialValues))\r\n }\r\n\r\n /* 恢复每个字段的值和状态 */\r\n fieldKeys.forEach(key => {\r\n /* 恢复为初始值的深拷贝 */\r\n valueRecord[key] = deepClone(initialValueRecord[key])\r\n\r\n /* 重置字段状态 */\r\n const field = fields[key]\r\n if (field) {\r\n field.touched = false /* 清除触碰状态 */\r\n field.dirty = false /* 清除脏值标记 */\r\n field.error = null /* 清除校验错误 */\r\n field.validating = false /* 清除校验中标记 */\r\n }\r\n })\r\n\r\n /* 清除提交错误 */\r\n submitError.value = null\r\n }\r\n\r\n /**\r\n * 重置单个字段\r\n * @param field - 要重置的字段名\r\n */\r\n function resetField(field: keyof T): void {\r\n const fieldKey = field as string\r\n /* 恢复字段值为初始值的深拷贝 */\r\n valueRecord[fieldKey] = deepClone(initialValueRecord[fieldKey])\r\n\r\n /* 重置字段状态 */\r\n const fieldState = fields[fieldKey]\r\n if (fieldState) {\r\n fieldState.touched = false\r\n fieldState.dirty = false\r\n fieldState.error = null\r\n fieldState.validating = false\r\n }\r\n }\r\n\r\n /* ── 返回所有状态和方法 ── */\r\n return {\r\n values /* 表单当前值(响应式) */,\r\n fields /* 字段状态映射(响应式) */,\r\n isDirty /* 是否有脏值 */,\r\n isValid /* 是否全部有效 */,\r\n isSubmitting /* 是否正在提交 */,\r\n submitError /* 提交错误信息 */,\r\n submitCount /* 提交次数 */,\r\n setFieldValue /* 设置字段值 */,\r\n setFieldTouched /* 标记字段已触碰 */,\r\n validateField /* 校验单个字段 */,\r\n validateAll /* 校验所有字段 */,\r\n handleSubmit /* 提交表单 */,\r\n reset /* 重置表单 */,\r\n resetField /* 重置单个字段 */,\r\n errors /* 错误信息映射 */,\r\n }\r\n}\r\n\r\n/* ============================================================\r\n * 预置校验器工厂函数\r\n * 提供常用的表单校验规则创建函数\r\n * ============================================================ */\r\n\r\n/**\r\n * 创建必填校验器\r\n * @param message - 自定义错误提示,默认为 '此字段为必填项'\r\n * @returns 校验器函数\r\n */\r\nexport function required(message = DEFAULT_REQUIRED_MESSAGE): FormValidator {\r\n return value => {\r\n /* 空字符串、null、undefined 均视为\"空\" */\r\n if (value === '' || value === null || value === undefined) {\r\n return message\r\n }\r\n\r\n return null /* 通过校验 */\r\n }\r\n}\r\n\r\n/**\r\n * 创建最小长度校验器\r\n * @param min - 最小字符数\r\n * @param message - 自定义错误提示\r\n * @returns 字符串校验器函数\r\n */\r\nexport function minLength(\r\n min: number,\r\n message?: string\r\n): FormValidator<string> {\r\n return value => {\r\n if (typeof value === 'string' && value.length < min) {\r\n return message ?? `至少需要 ${min} 个字符`\r\n }\r\n\r\n return null\r\n }\r\n}\r\n\r\n/**\r\n * 创建最大长度校验器\r\n * @param max - 最大字符数\r\n * @param message - 自定义错误提示\r\n * @returns 字符串校验器函数\r\n */\r\nexport function maxLength(\r\n max: number,\r\n message?: string\r\n): FormValidator<string> {\r\n return value => {\r\n if (typeof value === 'string' && value.length > max) {\r\n return message ?? `不能超过 ${max} 个字符`\r\n }\r\n\r\n return null\r\n }\r\n}\r\n\r\n/**\r\n * 创建正则匹配校验器\r\n * @param regex - 正则表达式\r\n * @param message - 自定义错误提示,默认为 '格式不正确'\r\n * @returns 字符串校验器函数\r\n */\r\nexport function pattern(\r\n regex: RegExp,\r\n message = DEFAULT_PATTERN_MESSAGE\r\n): FormValidator<string> {\r\n return value => {\r\n if (typeof value === 'string' && !regex.test(value)) {\r\n return message\r\n }\r\n\r\n return null\r\n }\r\n}\r\n\r\n/**\r\n * 创建邮箱格式校验器\r\n * 基于内置的 EMAIL_PATTERN 正则\r\n * @param message - 自定义错误提示,默认为 '邮箱格式不正确'\r\n * @returns 字符串校验器函数\r\n */\r\nexport function email(message = DEFAULT_EMAIL_MESSAGE): FormValidator<string> {\r\n return pattern(EMAIL_PATTERN, message)\r\n}\r\n\r\n/**\r\n * 创建数值范围校验器\r\n * @param min - 最小值\r\n * @param max - 最大值\r\n * @param message - 自定义错误提示\r\n * @returns 数字校验器函数\r\n */\r\nexport function numberRange(\r\n min: number,\r\n max: number,\r\n message?: string\r\n): FormValidator<number> {\r\n return value => {\r\n if (typeof value === 'number' && (value < min || value > max)) {\r\n return message ?? `必须在 ${min} 到 ${max} 之间`\r\n }\r\n\r\n return null\r\n }\r\n}\r\n","/** Optimistic state helper with an operation log for overlapping mutations. */\n\nimport type { Ref } from 'vue'\nimport { deepClone } from '@composy/store-core'\nimport { ref } from 'vue'\n\nexport interface OptimisticEntry<T> {\n id: string\n snapshot: T\n status: 'pending' | 'confirmed' | 'rolled-back'\n timestamp: number\n}\n\nexport interface UseOptimisticReturn<T> {\n state: Ref<T>\n isPending: Ref<boolean>\n pending: Ref<OptimisticEntry<T>[]>\n isRolledBack: Ref<boolean>\n mutate: (\n optimisticValue: T | ((prev: T) => T),\n confirm: () => Promise<void>\n ) => Promise<void>\n}\n\ninterface Operation<T> extends OptimisticEntry<T> {\n apply: (previous: T) => T\n}\n\nfunction uid(): string {\n return `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`\n}\n\nexport function useOptimistic<T>(initialState: T): UseOptimisticReturn<T> {\n const state = ref<T>(deepClone(initialState)) as Ref<T>\n let committedState = deepClone(initialState)\n const operations: Operation<T>[] = []\n const isPending = ref(false)\n const isRolledBack = ref(false)\n const pending = ref<OptimisticEntry<T>[]>([]) as Ref<OptimisticEntry<T>[]>\n\n const refresh = (): void => {\n let nextState = deepClone(committedState)\n operations.forEach(operation => {\n if (operation.status !== 'rolled-back') {\n nextState = operation.apply(nextState)\n }\n })\n state.value = deepClone(nextState)\n pending.value = operations\n .filter(operation => operation.status === 'pending')\n .map(operation => ({\n id: operation.id,\n snapshot: deepClone(operation.snapshot),\n status: operation.status,\n timestamp: operation.timestamp,\n }))\n isPending.value = pending.value.length > 0\n }\n\n const reconcile = (): void => {\n while (operations[0] && operations[0].status !== 'pending') {\n const operation = operations.shift()\n if (!operation || operation.status === 'rolled-back') continue\n committedState = operation.apply(deepClone(committedState))\n }\n refresh()\n }\n\n async function mutate(\n optimisticValue: T | ((prev: T) => T),\n confirm: () => Promise<void>\n ): Promise<void> {\n const snapshot = deepClone(state.value)\n const apply =\n typeof optimisticValue === 'function'\n ? (previous: T): T =>\n deepClone((optimisticValue as (prev: T) => T)(deepClone(previous)))\n : (): T => deepClone(optimisticValue)\n const operation: Operation<T> = {\n id: uid(),\n snapshot,\n status: 'pending',\n timestamp: Date.now(),\n apply,\n }\n\n operations.push(operation)\n isRolledBack.value = false\n refresh()\n\n try {\n await confirm()\n operation.status = 'confirmed'\n } catch {\n operation.status = 'rolled-back'\n isRolledBack.value = true\n } finally {\n reconcile()\n }\n }\n\n return {\n state,\n isPending,\n pending,\n isRolledBack,\n mutate,\n }\n}\n","/**\r\n * usePersist Composable\r\n *\r\n * 提供状态持久化功能\r\n * @module composables/usePersist\r\n */\r\n\r\nimport type { Ref } from 'vue'\r\nimport type { UsePersistOptions, UsePersistReturn } from '../types'\r\nimport { onMounted, ref, watch } from 'vue'\r\n\r\n/**\r\n * 获取存储对象\r\n */\r\nfunction getStorage(type: 'localStorage' | 'sessionStorage'): Storage | null {\r\n if (typeof window === 'undefined') {\r\n return null\r\n }\r\n try {\r\n return type === 'localStorage' ? window.localStorage : window.sessionStorage\r\n } catch {\r\n return null\r\n }\r\n}\r\n\r\n/**\r\n * 状态持久化 Hook\r\n *\r\n * 将状态自动保存到 localStorage 或 sessionStorage\r\n *\r\n * @template T - 值类型\r\n * @param options - 持久化选项\r\n * @returns 持久化操作对象\r\n *\r\n * @example\r\n * ```typescript\r\n * const { value, save, restore, clear } = usePersist<User>({\r\n * key: 'user-info',\r\n * storage: 'localStorage',\r\n * defaultValue: { name: '', age: 0 }\r\n * })\r\n *\r\n * // 更新值会自动保存\r\n * value.value = { name: 'John', age: 25 }\r\n *\r\n * // 手动保存\r\n * save()\r\n *\r\n * // 从存储恢复\r\n * restore()\r\n *\r\n * // 清除存储\r\n * clear()\r\n * ```\r\n */\r\nexport function usePersist<T>(options: UsePersistOptions): UsePersistReturn<T> {\r\n const {\r\n key,\r\n storage: storageType = 'localStorage',\r\n defaultValue,\r\n serialize = JSON.stringify,\r\n deserialize = JSON.parse,\r\n restoreOnMounted = true,\r\n } = options\r\n\r\n const storage = getStorage(storageType)\r\n const value = ref<T>(defaultValue as T) as Ref<T>\r\n\r\n /**\r\n * 从存储恢复\r\n */\r\n const restore = (): void => {\r\n if (!storage) return\r\n\r\n try {\r\n const stored = storage.getItem(key)\r\n if (stored !== null) {\r\n value.value = deserialize(stored) as T\r\n }\r\n } catch (error) {\r\n console.error(`[usePersist] 恢复失败 (${key}):`, error)\r\n }\r\n }\r\n\r\n /**\r\n * 保存到存储\r\n */\r\n const save = (): void => {\r\n if (!storage) return\r\n\r\n try {\r\n storage.setItem(key, serialize(value.value))\r\n } catch (error) {\r\n console.error(`[usePersist] 保存失败 (${key}):`, error)\r\n }\r\n }\r\n\r\n /**\r\n * 清除存储\r\n */\r\n const clear = (): void => {\r\n if (!storage) return\r\n\r\n try {\r\n storage.removeItem(key)\r\n value.value = defaultValue as T\r\n } catch (error) {\r\n console.error(`[usePersist] 清除失败 (${key}):`, error)\r\n }\r\n }\r\n\r\n // 组件挂载时恢复\r\n if (restoreOnMounted) {\r\n onMounted(() => {\r\n restore()\r\n })\r\n }\r\n\r\n // 监听变化自动保存\r\n watch(\r\n value,\r\n () => {\r\n save()\r\n },\r\n { deep: true }\r\n )\r\n\r\n return {\r\n value,\r\n save,\r\n restore,\r\n clear,\r\n }\r\n}\r\n\r\n/**\r\n * 创建持久化的 ref\r\n *\r\n * @template T - 值类型\r\n * @param key - 存储键名\r\n * @param defaultValue - 默认值\r\n * @param storage - 存储类型\r\n * @returns 持久化的 ref\r\n *\r\n * @example\r\n * ```typescript\r\n * const count = usePersistedRef('count', 0)\r\n *\r\n * // 更新会自动保存\r\n * count.value++\r\n * ```\r\n */\r\nexport function usePersistedRef<T>(\r\n key: string,\r\n defaultValue: T,\r\n storage: 'localStorage' | 'sessionStorage' = 'localStorage'\r\n): Ref<T> {\r\n const { value } = usePersist<T>({\r\n key,\r\n storage,\r\n defaultValue,\r\n })\r\n return value\r\n}\r\n\r\n/**\r\n * 创建带过期时间的持久化 ref\r\n *\r\n * @template T - 值类型\r\n * @param key - 存储键名\r\n * @param defaultValue - 默认值\r\n * @param ttl - 过期时间(毫秒)\r\n * @returns 持久化的 ref\r\n */\r\nexport function usePersistedRefWithTTL<T>(\r\n key: string,\r\n defaultValue: T,\r\n ttl: number\r\n): Ref<T> {\r\n interface StoredData {\r\n value: T\r\n expiry: number\r\n }\r\n\r\n const persisted = usePersist<StoredData>({\r\n key,\r\n defaultValue: { value: defaultValue, expiry: 0 },\r\n restoreOnMounted: false,\r\n serialize: (data: unknown) => JSON.stringify(data),\r\n deserialize: (str: string) => {\r\n const data = JSON.parse(str) as StoredData\r\n if (data.expiry && Date.now() > data.expiry) {\r\n return { value: defaultValue, expiry: 0 }\r\n }\r\n return data\r\n },\r\n })\r\n\r\n // 包装 ref 以自动设置过期时间\r\n persisted.restore()\r\n const wrappedRef = ref(persisted.value.value.value) as Ref<T>\r\n\r\n watch(wrappedRef, newValue => {\r\n persisted.value.value = {\r\n value: newValue,\r\n expiry: Date.now() + ttl,\r\n }\r\n })\r\n\r\n watch(\r\n persisted.value,\r\n stored => {\r\n if (!Object.is(wrappedRef.value, stored.value)) {\r\n wrappedRef.value = stored.value\r\n }\r\n },\r\n { deep: true }\r\n )\r\n\r\n return wrappedRef\r\n}\r\n","/* ============================================================\r\n * usePersistedState.ts\r\n * 持久化状态 Composable\r\n * 提供将 Vue 响应式状态自动持久化到 localStorage/sessionStorage 的能力\r\n * 支持 TTL 过期、跨标签页同步、防抖保存、序列化自定义等特性\r\n * ============================================================ */\r\n\r\n/* 从 Vue 导入 Ref 类型 */\r\nimport type { Ref } from 'vue'\r\n/* 从 store-core 导入防抖和深拷贝工具函数 */\r\nimport { debounce, deepClone } from '@composy/store-core'\r\n/* 从 Vue 导入生命周期钩子和响应式 API */\r\nimport { onUnmounted, ref, watch } from 'vue'\r\n\r\n/**\r\n * 持久化状态配置选项接口\r\n * @property key - 存储键名\r\n * @property storage - 存储类型,默认 'localStorage'\r\n * @property defaultValue - 默认值(当存储中无数据或过期时使用)\r\n * @serializer - 自定义序列化函数(默认 JSON.stringify)\r\n * @deserializer - 自定义反序列化函数(默认 JSON.parse)\r\n * @property syncTabs - 是否同步其他标签页的变化\r\n * @property debounce - 防抖延迟(毫秒),0 表示不防抖\r\n * @property deep - 是否深度监听状态变化\r\n * @property ttl - 存活时间(毫秒),过期后自动清除\r\n * @property onBeforeChange - 值变化前的钩子,返回 false 可阻止保存\r\n * @property onAfterChange - 值变化后的钩子\r\n * @property onBeforeRestore - 恢复前的钩子,可转换恢复的值\r\n * @property onAfterRestore - 恢复后的钩子\r\n */\r\nexport interface PersistedStateOptions<T> {\r\n key: string\r\n storage?: 'localStorage' | 'sessionStorage'\r\n defaultValue?: T\r\n serializer?: (value: T) => string\r\n deserializer?: (value: string) => T\r\n syncTabs?: boolean\r\n debounce?: number\r\n deep?: boolean\r\n ttl?: number\r\n onBeforeChange?: (newValue: T, oldValue: T) => boolean | void\r\n onAfterChange?: (newValue: T, oldValue: T) => void\r\n onBeforeRestore?: (value: T) => T\r\n onAfterRestore?: (value: T) => void\r\n}\r\n\r\n/**\r\n * 持久化状态返回值接口\r\n * @property state - 响应式状态引用\r\n * @property save - 手动保存到存储\r\n * @property restore - 从存储手动恢复\r\n * @property clear - 清除存储中的数据\r\n * @property reset - 重置为默认值并清除存储\r\n * @property isExpired - 检查存储数据是否已过期\r\n */\r\nexport interface PersistedStateReturn<T> {\r\n state: Ref<T>\r\n save: () => void\r\n restore: () => void\r\n clear: () => void\r\n reset: () => void\r\n isExpired: () => boolean\r\n}\r\n\r\n/**\r\n * 存储项的内部格式接口\r\n * 包装序列化后的值,附加时间戳和过期时间\r\n * @property value - 序列化后的字符串值\r\n * @property timestamp - 保存时间戳\r\n * @property expiresAt - 过期时间戳(可选,无则永不过期)\r\n */\r\ninterface StorageItem {\r\n value: string\r\n timestamp: number\r\n expiresAt?: number\r\n}\r\n\r\n/**\r\n * 默认序列化函数\r\n * 使用 JSON.stringify 将值转为 JSON 字符串\r\n */\r\nfunction defaultSerializer<T>(value: T): string {\r\n return JSON.stringify(value)\r\n}\r\n\r\n/**\r\n * 默认反序列化函数\r\n * 使用 JSON.parse 解析 JSON 字符串,解析失败则原样返回\r\n */\r\nfunction defaultDeserializer<T>(value: string): T {\r\n try {\r\n return JSON.parse(value)\r\n } catch {\r\n /* 解析失败(数据损坏),原样返回 */\r\n return value as T\r\n }\r\n}\r\n\r\n/**\r\n * 获取浏览器存储对象\r\n * 在非浏览器环境(如 SSR)返回 null\r\n * @param type - 存储类型\r\n * @returns Storage 对象或 null\r\n */\r\nfunction getStorage(type: 'localStorage' | 'sessionStorage'): Storage | null {\r\n /* SSR 环境下无 window 对象 */\r\n if (typeof window === 'undefined') {\r\n return null\r\n }\r\n\r\n return type === 'localStorage' ? window.localStorage : window.sessionStorage\r\n}\r\n\r\n/**\r\n * 持久化状态 Composable\r\n * 将 Vue 响应式状态自动持久化到浏览器存储\r\n * 支持 TTL 过期、跨标签页同步、防抖、生命周期钩子\r\n *\r\n * @param options - 配置选项\r\n * @returns 包含状态和操作方法的对象\r\n */\r\nexport function usePersistedState<T>(\r\n options: PersistedStateOptions<T>\r\n): PersistedStateReturn<T> {\r\n /* 解构配置选项,设置默认值 */\r\n const {\r\n key /* 存储键名 */,\r\n storage: storageType = 'localStorage' /* 存储类型,默认 localStorage */,\r\n defaultValue /* 默认值 */,\r\n serializer = defaultSerializer /* 序列化函数 */,\r\n deserializer = defaultDeserializer /* 反序列化函数 */,\r\n syncTabs = false /* 是否跨标签页同步 */,\r\n debounce: debounceDelay = 0 /* 防抖延迟 */,\r\n deep = true /* 是否深度监听 */,\r\n ttl /* 过期时间 */,\r\n onBeforeChange /* 变化前钩子 */,\r\n onAfterChange /* 变化后钩子 */,\r\n onBeforeRestore /* 恢复前钩子 */,\r\n onAfterRestore /* 恢复后钩子 */,\r\n } = options\r\n\r\n /* 获取实际的存储对象 */\r\n const storage = getStorage(storageType)\r\n /* 创建默认值的深拷贝工厂函数(避免共享引用) */\r\n const createDefaultValue = (): T => deepClone(defaultValue as T)\r\n /* 创建响应式状态,初始值为默认值的深拷贝 */\r\n const state = ref(createDefaultValue()) as Ref<T>\r\n /* 标记一次不应回写存储的内部状态同步 */\r\n let skipNextSave = false\r\n\r\n /**\r\n * 将当前状态保存到存储\r\n * 将状态序列化并包装为 StorageItem 格式(含时间戳和过期时间)\r\n */\r\n const save = (): void => {\r\n if (!storage) {\r\n return /* 存储不可用(SSR 环境) */\r\n }\r\n\r\n try {\r\n /* 构建存储项:包含序列化值、时间戳和可选的过期时间 */\r\n const item: StorageItem = {\r\n value: serializer(state.value) /* 序列化当前状态 */,\r\n timestamp: Date.now() /* 保存时间 */,\r\n expiresAt: ttl ? Date.now() + ttl : undefined /* 过期时间 */,\r\n }\r\n\r\n /* 将存储项 JSON 序列化后写入存储 */\r\n storage.setItem(key, JSON.stringify(item))\r\n } catch (error) {\r\n /* 存储可能已满或被禁用 */\r\n console.error(`[usePersistedState] 保存失败 (${key}):`, error)\r\n }\r\n }\r\n\r\n /* 如果设置了防抖延迟,创建防抖版的 save 函数 */\r\n const debouncedSave = debounceDelay > 0 ? debounce(save, debounceDelay) : null\r\n\r\n /**\r\n * 清除存储中的数据\r\n */\r\n const clear = (): void => {\r\n if (!storage) {\r\n return\r\n }\r\n\r\n try {\r\n storage.removeItem(key)\r\n } catch (error) {\r\n console.error(`[usePersistedState] 清除失败 (${key}):`, error)\r\n }\r\n }\r\n\r\n /**\r\n * 从存储恢复状态\r\n * 读取存储项,检查过期,执行恢复前钩子,更新响应式状态\r\n */\r\n const restore = (): void => {\r\n if (!storage) {\r\n return\r\n }\r\n\r\n try {\r\n /* 读取存储项 */\r\n const stored = storage.getItem(key)\r\n if (!stored) {\r\n return /* 存储中无数据 */\r\n }\r\n\r\n /* 解析存储项 */\r\n const item = JSON.parse(stored) as StorageItem\r\n /* 检查是否过期,过期则清除并退出 */\r\n if (item.expiresAt && Date.now() > item.expiresAt) {\r\n clear()\r\n return\r\n }\r\n\r\n /* 反序列化存储的值 */\r\n let value = deserializer(item.value)\r\n /* 执行恢复前钩子,允许转换恢复的值 */\r\n if (onBeforeRestore) {\r\n value = onBeforeRestore(value)\r\n }\r\n\r\n /* 更新响应式状态 */\r\n state.value = value\r\n /* 执行恢复后钩子 */\r\n onAfterRestore?.(value)\r\n } catch (error) {\r\n console.error(`[usePersistedState] 恢复失败 (${key}):`, error)\r\n }\r\n }\r\n\r\n /**\r\n * 重置为默认值并清除存储\r\n */\r\n const reset = (): void => {\r\n debouncedSave?.cancel()\r\n skipNextSave = true\r\n state.value = createDefaultValue() /* 恢复为默认值 */\r\n clear() /* 清除存储 */\r\n }\r\n\r\n /**\r\n * 检查存储数据是否已过期\r\n * @returns 是否已过期\r\n */\r\n const isExpired = (): boolean => {\r\n /* 无存储或无 TTL 则不可能过期 */\r\n if (!storage || !ttl) {\r\n return false\r\n }\r\n\r\n try {\r\n const stored = storage.getItem(key)\r\n if (!stored) {\r\n return false /* 无数据,视为未过期 */\r\n }\r\n\r\n const item = JSON.parse(stored) as StorageItem\r\n /* 比较当前时间与过期时间 */\r\n return item.expiresAt ? Date.now() > item.expiresAt : false\r\n } catch {\r\n return false\r\n }\r\n }\r\n\r\n /**\r\n * 处理跨标签页存储事件\r\n * 当其他标签页修改了同一 key 的存储时触发\r\n */\r\n const handleStorageEvent = (event: StorageEvent): void => {\r\n /* 只关注目标 key */\r\n if (event.key !== key) {\r\n return\r\n }\r\n\r\n /* 其他标签页删除 key 时同步恢复默认值,且不把默认值重新写回去 */\r\n if (!event.newValue) {\r\n debouncedSave?.cancel()\r\n skipNextSave = true\r\n state.value = createDefaultValue()\r\n return\r\n }\r\n\r\n try {\r\n const item = JSON.parse(event.newValue) as StorageItem\r\n /* 跳过已过期的数据 */\r\n if (item.expiresAt && Date.now() > item.expiresAt) {\r\n return\r\n }\r\n\r\n /* 反序列化并更新本地状态 */\r\n state.value = deserializer(item.value)\r\n } catch (error) {\r\n console.error(`[usePersistedState] 同步失败 (${key}):`, error)\r\n }\r\n }\r\n\r\n /* ── 初始化 ── */\r\n /* 组件挂载时立即尝试从存储恢复状态 */\r\n restore()\r\n\r\n /* ── 状态变化监听 ── */\r\n /* 监听 state 变化,自动保存到存储 */\r\n watch(\r\n state,\r\n (newValue, oldValue) => {\r\n if (skipNextSave) {\r\n skipNextSave = false\r\n return\r\n }\r\n\r\n /* 执行变化前钩子,返回 false 可阻止保存 */\r\n const canContinue = onBeforeChange?.(newValue, oldValue)\r\n if (canContinue === false) {\r\n return /* 阻止保存 */\r\n }\r\n\r\n /* 使用防抖保存或立即保存 */\r\n if (debouncedSave) {\r\n debouncedSave()\r\n } else {\r\n save()\r\n }\r\n\r\n /* 执行变化后钩子 */\r\n onAfterChange?.(newValue, oldValue)\r\n },\r\n { deep } /* 深度监听选项 */\r\n )\r\n\r\n /* ── 跨标签页同步 ── */\r\n /* 如果启用了跨标签页同步,监听 storage 事件 */\r\n if (syncTabs && typeof window !== 'undefined') {\r\n window.addEventListener('storage', handleStorageEvent)\r\n }\r\n\r\n /* ── 清理 ── */\r\n /* 组件卸载时取消防抖和移除事件监听 */\r\n onUnmounted(() => {\r\n /* 取消待执行的防抖保存 */\r\n debouncedSave?.cancel()\r\n /* 移除跨标签页同步的事件监听 */\r\n if (syncTabs && typeof window !== 'undefined') {\r\n window.removeEventListener('storage', handleStorageEvent)\r\n }\r\n })\r\n\r\n /* ── 返回状态和方法 ── */\r\n return {\r\n state /* 响应式状态 */,\r\n save /* 手动保存 */,\r\n restore /* 手动恢复 */,\r\n clear /* 清除存储 */,\r\n reset /* 重置为默认值 */,\r\n isExpired /* 检查是否过期 */,\r\n }\r\n}\r\n","/**\r\n * useSharedState - 跨标签页状态同步\r\n *\r\n * 基于 BroadcastChannel API 实现多标签页实时状态同步\r\n * @module composables/useSharedState\r\n */\r\n\r\nimport type { Ref } from 'vue'\r\nimport { deepClone, deepEqual } from '@composy/store-core'\r\nimport { onUnmounted, ref, watch } from 'vue'\r\n\r\n/**\r\n * useSharedState 选项\r\n */\r\nexport interface UseSharedStateOptions {\r\n /** 是否立即将当前值广播给其他标签页 */\r\n immediate?: boolean\r\n /** 是否深度监听变化 */\r\n deep?: boolean\r\n}\r\n\r\n/**\r\n * useSharedState 返回值\r\n */\r\nexport interface UseSharedStateReturn<T> {\r\n /** 响应式状态 (双向同步) */\r\n state: Ref<T>\r\n /** 手动广播当前状态到其他标签页 */\r\n broadcast: () => void\r\n /** 连接的标签页数量 (近似值) */\r\n peerCount: Ref<number>\r\n /** 最后同步时间 */\r\n lastSyncAt: Ref<number | null>\r\n /** 关闭通道 */\r\n close: () => void\r\n}\r\n\r\n/**\r\n * 跨标签页状态同步 Hook\r\n *\r\n * 使用 BroadcastChannel API 在多个标签页之间实时同步状态。\r\n * 任意标签页修改状态后,其他标签页会自动收到更新。\r\n *\r\n * @template T - 状态类型 (必须可序列化)\r\n * @param channelName - 通道名称 (相同名称的标签页共享状态)\r\n * @param initialState - 初始状态\r\n * @param options - 选项\r\n * @returns 同步状态和控制 API\r\n *\r\n * @example\r\n * ```typescript\r\n * // 标签页 A\r\n * const { state } = useSharedState('my-channel', { count: 0, user: 'Alice' })\r\n * state.value.count++ // 所有标签页都会收到更新\r\n *\r\n * // 标签页 B (相同代码)\r\n * const { state } = useSharedState('my-channel', { count: 0, user: 'Alice' })\r\n * // state.value.count 会自动变为 1\r\n *\r\n * // 手动广播\r\n * broadcast()\r\n *\r\n * // 关闭通道\r\n * close()\r\n * ```\r\n */\r\nexport function useSharedState<T>(\r\n channelName: string,\r\n initialState: T,\r\n options: UseSharedStateOptions = {}\r\n): UseSharedStateReturn<T> {\r\n const { immediate = false, deep = true } = options\r\n\r\n const state = ref<T>(deepClone(initialState)) as Ref<T>\r\n const peerCount = ref(0)\r\n const lastSyncAt = ref<number | null>(null)\r\n\r\n let channel: BroadcastChannel | null = null\r\n const originId = `${Date.now()}-${Math.random().toString(36).slice(2, 10)}`\r\n let revision = 0\r\n const latestRemoteRevision = new Map<string, number>()\r\n let pendingRemoteState: T\r\n let hasPendingRemoteState = false\r\n\r\n // 检测 BroadcastChannel 是否可用\r\n if (typeof BroadcastChannel === 'undefined') {\r\n console.warn(\r\n '[useSharedState] BroadcastChannel not supported in this environment'\r\n )\r\n return {\r\n state,\r\n broadcast: () => {},\r\n peerCount,\r\n lastSyncAt,\r\n close: () => {},\r\n }\r\n }\r\n\r\n try {\r\n channel = new BroadcastChannel(channelName)\r\n } catch (e) {\r\n console.warn('[useSharedState] Failed to create BroadcastChannel:', e)\r\n return {\r\n state,\r\n broadcast: () => {},\r\n peerCount,\r\n lastSyncAt,\r\n close: () => {},\r\n }\r\n }\r\n\r\n /** 广播当前状态 */\r\n function broadcast(): void {\r\n if (!channel) return\r\n try {\r\n channel.postMessage({\r\n type: 'state-update',\r\n payload: deepClone(state.value),\r\n timestamp: Date.now(),\r\n origin: originId,\r\n revision: ++revision,\r\n })\r\n } catch (e) {\r\n console.warn('[useSharedState] broadcast error:', e)\r\n }\r\n }\r\n\r\n /** 接收其他标签页的消息 */\r\n channel.onmessage = (event: MessageEvent) => {\r\n const msg = event.data\r\n if (!msg || typeof msg !== 'object') return\r\n\r\n if (msg.type === 'state-update') {\r\n if (msg.origin === originId) return\r\n\r\n const remoteRevision = typeof msg.revision === 'number' ? msg.revision : 0\r\n const previousRevision =\r\n latestRemoteRevision.get(String(msg.origin)) ?? -1\r\n if (remoteRevision <= previousRevision) return\r\n latestRemoteRevision.set(String(msg.origin), remoteRevision)\r\n\r\n pendingRemoteState = deepClone(msg.payload as T)\r\n hasPendingRemoteState = true\r\n state.value = pendingRemoteState\r\n lastSyncAt.value = msg.timestamp || Date.now()\r\n } else if (msg.type === 'ping') {\r\n channel?.postMessage({ type: 'pong' })\r\n } else if (msg.type === 'pong') {\r\n peerCount.value++\r\n }\r\n }\r\n\r\n /** 监听本地变化并广播 */\r\n const stopWatch = watch(\r\n state,\r\n () => {\r\n if (hasPendingRemoteState) {\r\n if (deepEqual(state.value, pendingRemoteState)) {\r\n hasPendingRemoteState = false\r\n return\r\n }\r\n hasPendingRemoteState = false\r\n }\r\n broadcast()\r\n },\r\n { deep }\r\n )\r\n\r\n /** 关闭通道 */\r\n function close(): void {\r\n stopWatch()\r\n if (channel) {\r\n channel.close()\r\n channel = null\r\n }\r\n }\r\n\r\n // 通知其他标签页本页存在\r\n if (immediate) {\r\n broadcast()\r\n }\r\n\r\n // 发送 ping 探测其他标签页\r\n if (channel) {\r\n peerCount.value = 0\r\n channel.postMessage({ type: 'ping' })\r\n }\r\n\r\n onUnmounted(() => {\r\n close()\r\n })\r\n\r\n return {\r\n state,\r\n broadcast,\r\n peerCount,\r\n lastSyncAt,\r\n close,\r\n }\r\n}\r\n","/**\r\n * useStore Composable\r\n *\r\n * 提供在组件中使用 Store 的便捷方法\r\n * @module composables/useStore\r\n */\r\n\r\nimport type { StoreGeneric } from 'pinia'\r\nimport type { Ref } from 'vue'\r\nimport { storeToRefs } from 'pinia'\r\nimport { computed, ref, watch } from 'vue'\r\n\r\n/**\r\n * useStore 选项\r\n */\r\nexport interface UseStoreOptions {\r\n /** 是否立即同步状态 */\r\n immediate?: boolean\r\n /** 是否深度监听 */\r\n deep?: boolean\r\n}\r\n\r\n/**\r\n * 通用 Store Hook\r\n *\r\n * 提供对 Pinia Store 的便捷访问\r\n *\r\n * @template T - Store 类型\r\n * @param useStoreHook - Pinia useStore hook\r\n * @param _options - 选项\r\n * @returns Store 包装对象\r\n *\r\n * @example\r\n * ```typescript\r\n * // 定义 Store\r\n * const useCounterStore = defineStore('counter', {\r\n * state: () => ({ count: 0 }),\r\n * actions: {\r\n * increment() { this.count++ }\r\n * }\r\n * })\r\n *\r\n * // 在组件中使用\r\n * const { store, refs, reset } = useStore(useCounterStore)\r\n *\r\n * // 访问响应式状态\r\n * console.log(refs.count.value)\r\n *\r\n * // 调用 action\r\n * store.increment()\r\n * ```\r\n */\r\nexport function useStore<T extends StoreGeneric>(\r\n useStoreHook: () => T,\r\n _options: UseStoreOptions = {}\r\n): {\r\n store: T\r\n refs: ReturnType<typeof storeToRefs<T>>\r\n reset: () => void\r\n patch: (partial: Partial<T> | ((state: T) => void)) => void\r\n} {\r\n const store = useStoreHook()\r\n\r\n // 获取响应式 refs\r\n const refs = storeToRefs(store)\r\n\r\n // 重置函数\r\n const reset = (): void => {\r\n if ('$reset' in store && typeof store.$reset === 'function') {\r\n ;(store.$reset as () => void)()\r\n }\r\n }\r\n\r\n // 批量更新函数\r\n const patch = (partial: Partial<T> | ((state: T) => void)): void => {\r\n if ('$patch' in store && typeof store.$patch === 'function') {\r\n ;(store.$patch as (partial: unknown) => void)(partial)\r\n }\r\n }\r\n\r\n return {\r\n store,\r\n refs,\r\n reset,\r\n patch,\r\n }\r\n}\r\n\r\n/**\r\n * 创建简单的响应式 Store\r\n *\r\n * 不依赖 Pinia,适用于简单场景\r\n *\r\n * @template S - 状态类型\r\n * @param initialState - 初始状态\r\n * @returns 响应式 Store\r\n *\r\n * @example\r\n * ```typescript\r\n * const { state, setState, reset } = useSimpleStore({\r\n * count: 0,\r\n * name: 'test'\r\n * })\r\n *\r\n * // 更新状态\r\n * setState({ count: 1 })\r\n *\r\n * // 重置\r\n * reset()\r\n * ```\r\n */\r\nexport function useSimpleStore<S extends Record<string, unknown>>(\r\n initialState: S\r\n): {\r\n state: Ref<S>\r\n setState: (partial: Partial<S>) => void\r\n reset: () => void\r\n subscribe: (callback: (state: S) => void) => () => void\r\n} {\r\n const state = ref({ ...initialState }) as Ref<S>\r\n const initialStateCopy = { ...initialState }\r\n\r\n const setState = (partial: Partial<S>): void => {\r\n Object.assign(state.value, partial)\r\n }\r\n\r\n const reset = (): void => {\r\n state.value = { ...initialStateCopy } as S\r\n }\r\n\r\n const subscribe = (callback: (state: S) => void): (() => void) => {\r\n const stop = watch(\r\n state,\r\n newState => {\r\n callback(newState)\r\n },\r\n { deep: true }\r\n )\r\n return stop\r\n }\r\n\r\n return {\r\n state,\r\n setState,\r\n reset,\r\n subscribe,\r\n }\r\n}\r\n\r\n/**\r\n * 创建计算属性 Store\r\n *\r\n * @template S - 状态类型\r\n * @template G - Getters 类型\r\n * @param state - 状态\r\n * @param getters - 计算属性\r\n * @returns 带计算属性的 Store\r\n */\r\nexport function useComputedStore<\r\n S extends Record<string, unknown>,\r\n G extends Record<string, (state: S) => unknown>,\r\n>(\r\n state: Ref<S>,\r\n getters: G\r\n): {\r\n state: Ref<S>\r\n getters: { [K in keyof G]: ReturnType<G[K]> }\r\n} {\r\n const computedGetters = {} as { [K in keyof G]: ReturnType<G[K]> }\r\n\r\n for (const key in getters) {\r\n ;(computedGetters as Record<string, unknown>)[key] = computed(() =>\r\n getters[key](state.value)\r\n )\r\n }\r\n\r\n return {\r\n state,\r\n getters: computedGetters,\r\n }\r\n}\r\n","import type { EventData } from '@composy/store-core'\r\nimport type { Pinia, StoreGeneric } from 'pinia'\r\nimport type { Ref } from 'vue'\r\nimport { deepClone, globalEventBus } from '@composy/store-core'\r\nimport { getActivePinia } from 'pinia'\r\nimport { onUnmounted, ref } from 'vue'\r\n\r\nconst STORE_REFRESH_EVENTS = new Set([\r\n 'store:created',\r\n 'store:disposed',\r\n 'state:changed',\r\n])\r\n\r\ntype PiniaWithStores = Pinia & {\r\n _s?: Map<string, StoreGeneric>\r\n}\r\n\r\nexport interface StoreInfo {\r\n id: string\r\n state: Record<string, unknown>\r\n active: boolean\r\n}\r\n\r\nexport interface EventLogEntry {\r\n type: string\r\n payload: unknown\r\n timestamp: number\r\n time: string\r\n}\r\n\r\nexport interface UseStoreDevtoolsReturn {\r\n stores: Ref<StoreInfo[]>\r\n eventLog: Ref<EventLogEntry[]>\r\n isMonitoring: Ref<boolean>\r\n startMonitoring: () => void\r\n stopMonitoring: () => void\r\n clearLog: () => void\r\n refreshStores: () => void\r\n getStoreState: (id: string) => Record<string, unknown> | null\r\n resetStore: (id: string) => boolean\r\n resetAllStores: () => void\r\n}\r\n\r\nfunction formatTime(timestamp: number): string {\r\n const date = new Date(timestamp)\r\n const hours = date.getHours().toString().padStart(2, '0')\r\n const minutes = date.getMinutes().toString().padStart(2, '0')\r\n const seconds = date.getSeconds().toString().padStart(2, '0')\r\n const milliseconds = date.getMilliseconds().toString().padStart(3, '0')\r\n\r\n return `${hours}:${minutes}:${seconds}.${milliseconds}`\r\n}\r\n\r\nfunction getPinia(): PiniaWithStores | null {\r\n return (getActivePinia() ?? null) as PiniaWithStores | null\r\n}\r\n\r\nfunction snapshotStore(store: StoreGeneric, id: string): StoreInfo {\r\n return {\r\n id,\r\n state: deepClone(store.$state),\r\n active: true,\r\n }\r\n}\r\n\r\nexport function useStoreDevtools(\r\n options: {\r\n /**\r\n * 最多保留多少条事件日志。\r\n */\r\n maxLogEntries?: number\r\n } = {}\r\n): UseStoreDevtoolsReturn {\r\n const { maxLogEntries = 200 } = options\r\n\r\n const stores = ref<StoreInfo[]>([])\r\n const eventLog = ref<EventLogEntry[]>([])\r\n const isMonitoring = ref(false)\r\n\r\n let unsubscribe: (() => void) | null = null\r\n\r\n function refreshStores(): void {\r\n const pinia = getPinia()\r\n if (!pinia?._s) {\r\n stores.value = []\r\n return\r\n }\r\n\r\n stores.value = Array.from(pinia._s.entries(), ([id, store]) => {\r\n return snapshotStore(store, id)\r\n })\r\n }\r\n\r\n function startMonitoring(): void {\r\n if (isMonitoring.value) {\r\n return\r\n }\r\n\r\n isMonitoring.value = true\r\n unsubscribe = globalEventBus.on('*', (data: EventData) => {\r\n const entry: EventLogEntry = {\r\n type: data.type,\r\n payload: data.payload,\r\n timestamp: data.timestamp,\r\n time: formatTime(data.timestamp),\r\n }\r\n\r\n eventLog.value = [entry, ...eventLog.value].slice(0, maxLogEntries)\r\n\r\n if (STORE_REFRESH_EVENTS.has(data.type)) {\r\n refreshStores()\r\n }\r\n })\r\n }\r\n\r\n function stopMonitoring(): void {\r\n if (!isMonitoring.value) {\r\n return\r\n }\r\n\r\n isMonitoring.value = false\r\n unsubscribe?.()\r\n unsubscribe = null\r\n }\r\n\r\n function clearLog(): void {\r\n eventLog.value = []\r\n }\r\n\r\n function getStoreState(id: string): Record<string, unknown> | null {\r\n const pinia = getPinia()\r\n const store = pinia?._s?.get(id)\r\n\r\n if (!store) {\r\n return null\r\n }\r\n\r\n return deepClone(store.$state)\r\n }\r\n\r\n function resetStore(id: string): boolean {\r\n const pinia = getPinia()\r\n const store = pinia?._s?.get(id)\r\n\r\n if (!store || typeof store.$reset !== 'function') {\r\n return false\r\n }\r\n\r\n store.$reset()\r\n refreshStores()\r\n return true\r\n }\r\n\r\n function resetAllStores(): void {\r\n const pinia = getPinia()\r\n if (!pinia?._s) {\r\n return\r\n }\r\n\r\n pinia._s.forEach(store => {\r\n if (typeof store.$reset === 'function') {\r\n store.$reset()\r\n }\r\n })\r\n\r\n refreshStores()\r\n }\r\n\r\n refreshStores()\r\n\r\n onUnmounted(() => {\r\n stopMonitoring()\r\n })\r\n\r\n return {\r\n stores,\r\n eventLog,\r\n isMonitoring,\r\n startMonitoring,\r\n stopMonitoring,\r\n clearLog,\r\n refreshStores,\r\n getStoreState,\r\n resetStore,\r\n resetAllStores,\r\n }\r\n}\r\n","/**\r\n * useStoreSnapshot - Store 状态快照工具\r\n *\r\n * 提供状态快照、历史记录和调试功能\r\n * @module composables/useStoreSnapshot\r\n */\r\n\r\nimport type { ComputedRef, Ref } from 'vue'\r\nimport { deepClone, deepEqual } from '@composy/store-core'\r\nimport { computed, onUnmounted, ref, watch } from 'vue'\r\n\r\n/**\r\n * 状态快照\r\n */\r\nexport interface StateSnapshot<S = unknown> {\r\n /** 快照 ID */\r\n id: string\r\n /** 状态数据 */\r\n state: S\r\n /** 时间戳 */\r\n timestamp: number\r\n /** 快照标签 */\r\n label?: string\r\n /** 元数据 */\r\n meta?: Record<string, unknown>\r\n}\r\n\r\n/**\r\n * 状态差异\r\n */\r\nexport interface StateDiff {\r\n /** 差异路径 */\r\n path: string\r\n /** 旧值 */\r\n oldValue: unknown\r\n /** 新值 */\r\n newValue: unknown\r\n /** 差异类型 */\r\n type: 'add' | 'remove' | 'change'\r\n}\r\n\r\n/**\r\n * useStoreSnapshot 选项\r\n */\r\nexport interface UseStoreSnapshotOptions {\r\n /** 最大快照数量 */\r\n maxSnapshots?: number\r\n /** 是否自动记录 */\r\n autoRecord?: boolean\r\n /** 自动记录防抖延迟(毫秒) */\r\n debounceMs?: number\r\n /** 是否深度监听 */\r\n deep?: boolean\r\n}\r\n\r\n/**\r\n * useStoreSnapshot 返回值\r\n */\r\nexport interface UseStoreSnapshotReturn<S> {\r\n /** 快照列表 */\r\n snapshots: Ref<Array<StateSnapshot<S>>>\r\n /** 当前快照索引 */\r\n currentIndex: Ref<number>\r\n /** 是否可以撤销 */\r\n canUndo: ComputedRef<boolean>\r\n /** 是否可以重做 */\r\n canRedo: ComputedRef<boolean>\r\n /** 创建快照 */\r\n takeSnapshot: (\r\n label?: string,\r\n meta?: Record<string, unknown>\r\n ) => StateSnapshot<S>\r\n /** 恢复快照 */\r\n restoreSnapshot: (id: string) => boolean\r\n /** 撤销 */\r\n undo: () => boolean\r\n /** 重做 */\r\n redo: () => boolean\r\n /** 跳转到指定快照 */\r\n goto: (index: number) => boolean\r\n /** 清空快照 */\r\n clearSnapshots: () => void\r\n /** 获取状态差异 */\r\n getDiff: (snapshotId1: string, snapshotId2: string) => StateDiff[]\r\n /** 导出快照 */\r\n exportSnapshots: () => string\r\n /** 导入快照 */\r\n importSnapshots: (data: string) => boolean\r\n}\r\n\r\n/**\r\n * 生成唯一 ID\r\n */\r\nfunction generateId(): string {\r\n return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`\r\n}\r\n\r\n/**\r\n * 计算两个对象的差异\r\n */\r\nfunction computeDiff(oldObj: unknown, newObj: unknown, path = ''): StateDiff[] {\r\n const diffs: StateDiff[] = []\r\n\r\n // 类型不同\r\n if (typeof oldObj !== typeof newObj) {\r\n diffs.push({\r\n path: path || '.',\r\n oldValue: oldObj,\r\n newValue: newObj,\r\n type: 'change',\r\n })\r\n return diffs\r\n }\r\n\r\n // 基础类型比较\r\n if (typeof oldObj !== 'object' || oldObj === null || newObj === null) {\r\n if (!Object.is(oldObj, newObj)) {\r\n diffs.push({\r\n path: path || '.',\r\n oldValue: oldObj,\r\n newValue: newObj,\r\n type: 'change',\r\n })\r\n }\r\n return diffs\r\n }\r\n\r\n // 数组比较\r\n if (Array.isArray(oldObj) && Array.isArray(newObj)) {\r\n const maxLen = Math.max(oldObj.length, newObj.length)\r\n for (let i = 0; i < maxLen; i++) {\r\n const itemPath = path ? `${path}[${i}]` : `[${i}]`\r\n if (i >= oldObj.length) {\r\n diffs.push({\r\n path: itemPath,\r\n oldValue: undefined,\r\n newValue: newObj[i],\r\n type: 'add',\r\n })\r\n } else if (i >= newObj.length) {\r\n diffs.push({\r\n path: itemPath,\r\n oldValue: oldObj[i],\r\n newValue: undefined,\r\n type: 'remove',\r\n })\r\n } else {\r\n diffs.push(...computeDiff(oldObj[i], newObj[i], itemPath))\r\n }\r\n }\r\n return diffs\r\n }\r\n\r\n // 对象比较\r\n const oldKeys = new Set(Object.keys(oldObj as Record<string, unknown>))\r\n const newKeys = new Set(Object.keys(newObj as Record<string, unknown>))\r\n\r\n // 检查新增的键\r\n for (const key of newKeys) {\r\n if (!oldKeys.has(key)) {\r\n diffs.push({\r\n path: path ? `${path}.${key}` : key,\r\n oldValue: undefined,\r\n newValue: (newObj as Record<string, unknown>)[key],\r\n type: 'add',\r\n })\r\n }\r\n }\r\n\r\n // 检查删除的键\r\n for (const key of oldKeys) {\r\n if (!newKeys.has(key)) {\r\n diffs.push({\r\n path: path ? `${path}.${key}` : key,\r\n oldValue: (oldObj as Record<string, unknown>)[key],\r\n newValue: undefined,\r\n type: 'remove',\r\n })\r\n }\r\n }\r\n\r\n // 检查修改的键\r\n for (const key of oldKeys) {\r\n if (newKeys.has(key)) {\r\n diffs.push(\r\n ...computeDiff(\r\n (oldObj as Record<string, unknown>)[key],\r\n (newObj as Record<string, unknown>)[key],\r\n path ? `${path}.${key}` : key\r\n )\r\n )\r\n }\r\n }\r\n\r\n return diffs\r\n}\r\n\r\n/**\r\n * Store 状态快照 Hook\r\n *\r\n * 提供状态快照、撤销/重做和调试功能\r\n *\r\n * @template S - 状态类型\r\n * @param state - 响应式状态\r\n * @param setState - 状态设置函数\r\n * @param options - 配置选项\r\n * @returns 快照操作对象\r\n *\r\n * @example\r\n * ```typescript\r\n * const store = useCounterStore()\r\n *\r\n * const {\r\n * snapshots,\r\n * canUndo,\r\n * canRedo,\r\n * takeSnapshot,\r\n * undo,\r\n * redo,\r\n * getDiff\r\n * } = useStoreSnapshot(\r\n * () => store.$state,\r\n * (state) => store.$patch(state),\r\n * { maxSnapshots: 50, autoRecord: true }\r\n * )\r\n *\r\n * // 手动创建快照\r\n * takeSnapshot('保存点 1')\r\n *\r\n * // 撤销操作\r\n * if (canUndo.value) {\r\n * undo()\r\n * }\r\n *\r\n * // 获取状态差异\r\n * const diff = getDiff(snapshots.value[0].id, snapshots.value[1].id)\r\n * ```\r\n */\r\nexport function useStoreSnapshot<S>(\r\n state: Ref<S> | (() => S),\r\n setState: (state: S) => void,\r\n options: UseStoreSnapshotOptions = {}\r\n): UseStoreSnapshotReturn<S> {\r\n const {\r\n maxSnapshots = 100,\r\n autoRecord = false,\r\n debounceMs = 300,\r\n deep = true,\r\n } = options\r\n\r\n const snapshots = ref<Array<StateSnapshot<S>>>([]) as Ref<\r\n Array<StateSnapshot<S>>\r\n >\r\n const currentIndex = ref(-1)\r\n let debounceTimer: ReturnType<typeof setTimeout> | undefined\r\n let isRestoring = false\r\n let previousAutoRecordState: S | undefined\r\n\r\n // 获取当前状态\r\n const getState = (): S => {\r\n return typeof state === 'function' ? state() : state.value\r\n }\r\n\r\n // 是否可以撤销\r\n const canUndo = computed(() => currentIndex.value > 0)\r\n\r\n // 是否可以重做\r\n const canRedo = computed(\r\n () => currentIndex.value < snapshots.value.length - 1\r\n )\r\n\r\n /**\r\n * 创建快照\r\n */\r\n const takeSnapshot = (\r\n label?: string,\r\n meta?: Record<string, unknown>\r\n ): StateSnapshot<S> => {\r\n const currentState = getState()\r\n const snapshot: StateSnapshot<S> = {\r\n id: generateId(),\r\n state: deepClone(currentState),\r\n timestamp: Date.now(),\r\n label,\r\n meta,\r\n }\r\n\r\n // 如果当前不在最新位置,删除后面的快照\r\n if (currentIndex.value < snapshots.value.length - 1) {\r\n snapshots.value = snapshots.value.slice(0, currentIndex.value + 1)\r\n }\r\n\r\n // 添加新快照\r\n snapshots.value.push(snapshot)\r\n currentIndex.value = snapshots.value.length - 1\r\n\r\n // 限制快照数量\r\n if (snapshots.value.length > maxSnapshots) {\r\n snapshots.value.shift()\r\n currentIndex.value--\r\n }\r\n\r\n return snapshot\r\n }\r\n\r\n /**\r\n * 恢复快照\r\n */\r\n const restoreSnapshot = (id: string): boolean => {\r\n const index = snapshots.value.findIndex(s => s.id === id)\r\n if (index === -1) return false\r\n\r\n isRestoring = true\r\n try {\r\n setState(deepClone(snapshots.value[index].state))\r\n currentIndex.value = index\r\n previousAutoRecordState = deepClone(getState())\r\n return true\r\n } finally {\r\n isRestoring = false\r\n }\r\n }\r\n\r\n /**\r\n * 撤销\r\n */\r\n const undo = (): boolean => {\r\n if (!canUndo.value) return false\r\n\r\n const targetIndex = currentIndex.value - 1\r\n isRestoring = true\r\n try {\r\n setState(deepClone(snapshots.value[targetIndex].state))\r\n currentIndex.value = targetIndex\r\n previousAutoRecordState = deepClone(getState())\r\n return true\r\n } finally {\r\n isRestoring = false\r\n }\r\n }\r\n\r\n /**\r\n * 重做\r\n */\r\n const redo = (): boolean => {\r\n if (!canRedo.value) return false\r\n\r\n const targetIndex = currentIndex.value + 1\r\n isRestoring = true\r\n try {\r\n setState(deepClone(snapshots.value[targetIndex].state))\r\n currentIndex.value = targetIndex\r\n previousAutoRecordState = deepClone(getState())\r\n return true\r\n } finally {\r\n isRestoring = false\r\n }\r\n }\r\n\r\n /**\r\n * 跳转到指定快照\r\n */\r\n const goto = (index: number): boolean => {\r\n if (index < 0 || index >= snapshots.value.length) return false\r\n\r\n isRestoring = true\r\n try {\r\n setState(deepClone(snapshots.value[index].state))\r\n currentIndex.value = index\r\n previousAutoRecordState = deepClone(getState())\r\n return true\r\n } finally {\r\n isRestoring = false\r\n }\r\n }\r\n\r\n /**\r\n * 清空快照\r\n */\r\n const clearSnapshots = (): void => {\r\n snapshots.value = []\r\n currentIndex.value = -1\r\n }\r\n\r\n /**\r\n * 获取状态差异\r\n */\r\n const getDiff = (snapshotId1: string, snapshotId2: string): StateDiff[] => {\r\n const snapshot1 = snapshots.value.find(s => s.id === snapshotId1)\r\n const snapshot2 = snapshots.value.find(s => s.id === snapshotId2)\r\n\r\n if (!snapshot1 || !snapshot2) return []\r\n\r\n return computeDiff(snapshot1.state, snapshot2.state)\r\n }\r\n\r\n /**\r\n * 导出快照\r\n */\r\n const exportSnapshots = (): string => {\r\n return JSON.stringify({\r\n snapshots: snapshots.value,\r\n currentIndex: currentIndex.value,\r\n exportedAt: Date.now(),\r\n })\r\n }\r\n\r\n /**\r\n * 导入快照\r\n */\r\n const importSnapshots = (data: string): boolean => {\r\n try {\r\n const parsed = JSON.parse(data) as {\r\n snapshots: Array<StateSnapshot<S>>\r\n currentIndex: number\r\n }\r\n\r\n if (!Array.isArray(parsed.snapshots)) return false\r\n\r\n snapshots.value = parsed.snapshots\r\n currentIndex.value = Math.min(\r\n parsed.currentIndex ?? snapshots.value.length - 1,\r\n snapshots.value.length - 1\r\n )\r\n\r\n return true\r\n } catch {\r\n return false\r\n }\r\n }\r\n\r\n // 自动记录\r\n if (autoRecord) {\r\n const stateRef = typeof state === 'function' ? computed(state) : state\r\n previousAutoRecordState = deepClone(getState())\r\n\r\n const stopWatch = watch(\r\n stateRef,\r\n (newState, oldState) => {\r\n // 如果是恢复操作,不记录\r\n if (isRestoring) return\r\n\r\n // 如果状态未变化,不记录\r\n if (deepEqual(newState, previousAutoRecordState ?? oldState)) return\r\n previousAutoRecordState = deepClone(newState)\r\n\r\n // 防抖记录\r\n if (debounceTimer) {\r\n clearTimeout(debounceTimer)\r\n }\r\n\r\n debounceTimer = setTimeout(() => {\r\n takeSnapshot()\r\n }, debounceMs)\r\n },\r\n { deep }\r\n )\r\n\r\n // 初始快照\r\n takeSnapshot('初始状态')\r\n\r\n onUnmounted(() => {\r\n stopWatch()\r\n if (debounceTimer) {\r\n clearTimeout(debounceTimer)\r\n }\r\n })\r\n }\r\n\r\n return {\r\n snapshots,\r\n currentIndex,\r\n canUndo,\r\n canRedo,\r\n takeSnapshot,\r\n restoreSnapshot,\r\n undo,\r\n redo,\r\n goto,\r\n clearSnapshots,\r\n getDiff,\r\n exportSnapshots,\r\n importSnapshots,\r\n }\r\n}\r\n","/**\r\n * useSubscribe Composable\r\n *\r\n * 提供 Store 订阅功能\r\n * @module composables/useSubscribe\r\n */\r\n\r\nimport type { Ref, WatchStopHandle } from 'vue'\r\nimport type { MutationInfo, SubscribeOptions } from '../types'\r\nimport { onUnmounted, watch } from 'vue'\r\n\r\n/**\r\n * 订阅 Store 状态变化\r\n *\r\n * @template S - 状态类型\r\n * @param store - Pinia Store 实例\r\n * @param callback - 回调函数\r\n * @param options - 订阅选项\r\n * @returns 取消订阅函数\r\n *\r\n * @example\r\n * ```typescript\r\n * const store = useCounterStore()\r\n *\r\n * // 订阅状态变化\r\n * const unsubscribe = useSubscribe(store, (mutation, state) => {\r\n * console.log('State changed:', mutation.type, state)\r\n * })\r\n *\r\n * // 取消订阅\r\n * unsubscribe()\r\n * ```\r\n */\r\nexport function useSubscribe<S extends Record<string, unknown>>(\r\n store: S & {\r\n $subscribe?: (\r\n callback: (mutation: MutationInfo<S>, state: S) => void,\r\n options?: { detached?: boolean }\r\n ) => () => void\r\n },\r\n callback: (mutation: MutationInfo<S>, state: S) => void,\r\n options: SubscribeOptions = {}\r\n): () => void {\r\n const { detached = false } = options\r\n\r\n // 使用 Pinia 的 $subscribe\r\n if (store.$subscribe) {\r\n const unsubscribe = store.$subscribe(\r\n (mutation, state) => {\r\n callback(mutation as MutationInfo<S>, state as S)\r\n },\r\n { detached }\r\n )\r\n\r\n // 组件卸载时自动取消订阅\r\n if (!detached) {\r\n onUnmounted(() => {\r\n unsubscribe()\r\n })\r\n }\r\n\r\n return unsubscribe\r\n }\r\n\r\n // 如果不是 Pinia Store,返回空函数\r\n console.warn('[useSubscribe] Store 不支持 $subscribe 方法')\r\n return () => {}\r\n}\r\n\r\n/**\r\n * 订阅 Store Action 调用\r\n *\r\n * @template S - Store 类型\r\n * @param store - Pinia Store 实例\r\n * @param callback - 回调函数\r\n * @returns 取消订阅函数\r\n *\r\n * @example\r\n * ```typescript\r\n * const store = useCounterStore()\r\n *\r\n * // 订阅 Action 调用\r\n * useOnAction(store, ({ name, args, after, onError }) => {\r\n * console.log(`Action ${name} called with:`, args)\r\n *\r\n * after((result) => {\r\n * console.log(`Action ${name} finished with:`, result)\r\n * })\r\n *\r\n * onError((error) => {\r\n * console.error(`Action ${name} failed:`, error)\r\n * })\r\n * })\r\n * ```\r\n */\r\nexport function useOnAction<S extends Record<string, unknown>>(\r\n store: S & {\r\n $onAction?: (\r\n callback: (context: ActionContext) => void,\r\n detached?: boolean\r\n ) => () => void\r\n },\r\n callback: (context: ActionContext) => void,\r\n detached = false\r\n): () => void {\r\n if (store.$onAction) {\r\n const unsubscribe = store.$onAction(callback, detached)\r\n\r\n if (!detached) {\r\n onUnmounted(() => {\r\n unsubscribe()\r\n })\r\n }\r\n\r\n return unsubscribe\r\n }\r\n\r\n console.warn('[useOnAction] Store 不支持 $onAction 方法')\r\n return () => {}\r\n}\r\n\r\n/**\r\n * Action 上下文\r\n */\r\nexport interface ActionContext {\r\n /** Action 名称 */\r\n name: string\r\n /** Store 实例 */\r\n store: unknown\r\n /** Action 参数 */\r\n args: unknown[]\r\n /** Action 完成后回调 */\r\n after: (callback: (result: unknown) => void) => void\r\n /** Action 错误回调 */\r\n onError: (callback: (error: unknown) => void) => void\r\n}\r\n\r\n/**\r\n * 监听 ref 变化\r\n *\r\n * @template T - 值类型\r\n * @param source - 监听源\r\n * @param callback - 回调函数\r\n * @param options - 监听选项\r\n * @returns 停止监听函数\r\n */\r\nexport function useWatch<T>(\r\n source: Ref<T>,\r\n callback: (newValue: T, oldValue: T | undefined) => void,\r\n options: SubscribeOptions = {}\r\n): WatchStopHandle {\r\n const { immediate = false, deep = false, detached = false } = options\r\n\r\n const stop = watch(\r\n source,\r\n (newValue, oldValue) => {\r\n callback(newValue, oldValue)\r\n },\r\n { immediate, deep }\r\n )\r\n\r\n if (!detached) {\r\n onUnmounted(() => {\r\n stop()\r\n })\r\n }\r\n\r\n return stop\r\n}\r\n","// 从 pinia 导入 Pinia 类型\r\nimport type { Pinia } from 'pinia'\r\n// 从 vue 导入 InjectionKey 类型,用于类型安全的依赖注入\r\nimport type { InjectionKey } from 'vue'\r\n// 导入 StoreRuntime 类型,表示 store 运行时实例\r\nimport type { StoreRuntime } from '../runtime/store-runtime'\r\n// 导入 GlobalStoreAPI 类型,表示全局 store API 接口\r\nimport type { GlobalStoreAPI } from '../types'\r\n\r\n// store 全局 API 的注入键符号,用于 provide/inject 传递 GlobalStoreAPI 实例\r\nexport const STORE_API_SYMBOL = Symbol(\r\n 'ldesign-store-api'\r\n) as InjectionKey<GlobalStoreAPI>\r\n// store Pinia 实例的注入键符号,用于 provide/inject 传递 Pinia 实例\r\nexport const STORE_PINIA_SYMBOL = Symbol(\r\n 'ldesign-store-pinia'\r\n) as InjectionKey<Pinia>\r\n// store 运行时实例的注入键符号,用于 provide/inject 传递 StoreRuntime 实例\r\nexport const STORE_RUNTIME_SYMBOL = Symbol(\r\n 'ldesign-store-runtime'\r\n) as InjectionKey<StoreRuntime>\r\n","/* ============================================================\r\n * pinia-plugin.ts\r\n * Pinia 持久化插件\r\n * 提供将 Pinia Store 状态自动持久化到浏览器存储的能力\r\n * 支持 TTL 过期、跨标签页同步、include/exclude 路径过滤、\r\n * 防抖保存、$dispose 生命周期钩子等特性\r\n * ============================================================ */\r\n\r\n/* 从 Pinia 导入插件相关类型 */\r\nimport type { PiniaPlugin, PiniaPluginContext, StoreGeneric } from 'pinia'\r\n/* 从本地类型文件导入持久化选项 */\r\nimport type { PersistOptions } from '../types'\r\n/* 从 store-core 导入工具函数 */\r\nimport {\r\n debounce,\r\n deepClone,\r\n deepMerge,\r\n deleteByPath,\r\n getByPath,\r\n setByPath,\r\n} from '@composy/store-core'\r\n\r\n/* 用于标记 store 的 $dispose 是否已被 patch 的 Symbol 键 */\r\nconst STORE_PERSIST_DISPOSE_KEY = Symbol('ldesign-store-persist-dispose')\r\n\r\n/* 浏览器存储类型 */\r\ntype BrowserStorage = 'localStorage' | 'sessionStorage'\r\n\r\n/* 可被 patch $dispose 的 Store 扩展类型 */\r\ntype DisposableStore = StoreGeneric & {\r\n [STORE_PERSIST_DISPOSE_KEY]?: () => void /* 恢复原始 $dispose 的函数 */\r\n}\r\n\r\n/**\r\n * 持久化存储项的内部格式接口\r\n * 包含序列化的状态、时间戳、过期时间和版本号\r\n */\r\ninterface PersistedStorageItem {\r\n state: string /* 序列化后的状态字符串 */\r\n timestamp: number /* 保存时间戳 */\r\n expiresAt?: number /* 过期时间戳(可选) */\r\n version: number /* 数据格式版本号 */\r\n}\r\n\r\n/**\r\n * 持久化插件控制器接口\r\n * @property plugin - Pinia 插件函数\r\n * @property dispose - 清理函数,用于移除所有持久化订阅\r\n */\r\ninterface PersistPluginController {\r\n plugin: PiniaPlugin\r\n dispose: () => void\r\n}\r\n\r\n/**\r\n * 持久化插件全局配置选项\r\n * @property storage - 默认存储类型\r\n * @property keyPrefix - 存储键名前缀\r\n * @property debounce - 防抖延迟(毫秒)\r\n * @property debug - 是否开启调试日志\r\n * @property syncTabs - 是否默认启用跨标签页同步\r\n * @property defaultTTL - 默认过期时间(毫秒)\r\n */\r\nexport interface PersistPluginOptions {\r\n storage?: BrowserStorage\r\n keyPrefix?: string\r\n debounce?: number\r\n debug?: boolean\r\n syncTabs?: boolean\r\n defaultTTL?: number\r\n}\r\n\r\n/* 增强版持久化插件选项(与基础版相同,预留扩展) */\r\nexport interface EnhancedPersistPluginOptions extends PersistPluginOptions {}\r\n\r\n/**\r\n * 增强版持久化选项(Store 级别配置)\r\n * 在基础 PersistOptions 上增加 excludePaths、syncTabs、ttl\r\n */\r\nexport interface EnhancedPersistOptions extends PersistOptions {\r\n syncTabs?: boolean\r\n ttl?: number\r\n excludePaths?: string[]\r\n}\r\n\r\n/* 活跃的持久化控制器集合,用于全局清理 */\r\nconst activePersistControllers = new Set<() => void>()\r\n\r\n/**\r\n * 获取浏览器存储对象\r\n * 在 SSR 环境下返回 null\r\n */\r\nfunction getStorage(type: BrowserStorage): Storage | null {\r\n if (typeof window === 'undefined') {\r\n return null\r\n }\r\n\r\n try {\r\n return type === 'localStorage' ? window.localStorage : window.sessionStorage\r\n } catch {\r\n return null\r\n }\r\n}\r\n\r\n/**\r\n * 规范化 Store 的 persist 配置\r\n * 将 boolean/对象/undefined 统一为 EnhancedPersistOptions | null\r\n */\r\nfunction normalizePersistConfig(\r\n value: boolean | PersistOptions | EnhancedPersistOptions | undefined\r\n): EnhancedPersistOptions | null {\r\n if (!value) {\r\n return null /* 未配置持久化 */\r\n }\r\n\r\n /* boolean true 表示使用默认配置的空对象 */\r\n return typeof value === 'boolean' ? {} : value\r\n}\r\n\r\n/**\r\n * 根据 include/exclude 路径过滤状态\r\n * 优先使用 includePaths(白名单),其次使用 excludePaths(黑名单)\r\n *\r\n * @param state - 完整状态对象\r\n * @param includePaths - 包含路径列表(白名单模式)\r\n * @param excludePaths - 排除路径列表(黑名单模式)\r\n * @returns 过滤后的状态对象\r\n */\r\nfunction filterState(\r\n state: Record<string, unknown>,\r\n includePaths?: string[],\r\n excludePaths?: string[]\r\n): Record<string, unknown> {\r\n /* 白名单模式:只保留指定路径 */\r\n if (includePaths?.length) {\r\n const pickedState: Record<string, unknown> = {}\r\n includePaths.forEach(path => {\r\n const value = getByPath(state, path)\r\n if (value !== undefined) {\r\n setByPath(pickedState, path, deepClone(value))\r\n }\r\n })\r\n return pickedState\r\n }\r\n\r\n /* 黑名单模式:排除指定路径 */\r\n if (excludePaths?.length) {\r\n const nextState = deepClone(state)\r\n excludePaths.forEach(path => {\r\n deleteByPath(nextState, path)\r\n })\r\n return nextState\r\n }\r\n\r\n /* 无过滤:返回状态的深拷贝 */\r\n return deepClone(state)\r\n}\r\n\r\n/**\r\n * 将持久化的状态恢复到 Store\r\n * 使用 $patch + deepMerge 避免覆盖整个 state 引用\r\n */\r\nfunction restoreStoreState(\r\n store: StoreGeneric,\r\n nextState: Record<string, unknown>\r\n): void {\r\n store.$patch(state => {\r\n deepMerge(state as Record<string, unknown>, nextState)\r\n })\r\n}\r\n\r\n/**\r\n * 为 Store 的 $dispose 方法打补丁\r\n * 在 Store 被销毁时自动执行清理逻辑(取消订阅、移除事件监听等)\r\n *\r\n * @param store - 目标 Store\r\n * @param cleanup - 清理回调函数\r\n */\r\nfunction patchPersistDispose(\r\n store: DisposableStore,\r\n cleanup: () => void\r\n): void {\r\n /* 如果之前已有补丁,先恢复原始方法 */\r\n const restorePatchedDispose = store[STORE_PERSIST_DISPOSE_KEY]\r\n if (restorePatchedDispose) {\r\n restorePatchedDispose()\r\n }\r\n\r\n /* 保存原始 $dispose 方法 */\r\n const originalDispose = store.$dispose\r\n /* 替换为带清理逻辑的版本 */\r\n store.$dispose = function patchedDispose() {\r\n cleanup() /* 先执行清理 */\r\n return originalDispose.call(this) /* 再调用原始 dispose */\r\n }\r\n\r\n /* 保存恢复函数,供后续补丁使用 */\r\n store[STORE_PERSIST_DISPOSE_KEY] = () => {\r\n store.$dispose = originalDispose\r\n delete store[STORE_PERSIST_DISPOSE_KEY]\r\n }\r\n}\r\n\r\n/**\r\n * 创建持久化插件控制器\r\n * 返回一个 Pinia 插件函数和对应的 dispose 清理函数\r\n *\r\n * @param options - 插件全局配置\r\n * @returns 控制器对象(包含 plugin 和 dispose)\r\n */\r\nexport function createPersistPluginController(\r\n options: PersistPluginOptions = {}\r\n): PersistPluginController {\r\n /* 解构配置选项,设置默认值 */\r\n const {\r\n storage: defaultStorage = 'localStorage' /* 默认存储类型 */,\r\n keyPrefix = 'ldesign-store:' /* 存储键前缀 */,\r\n debounce: debounceDelay = 100 /* 默认 100ms 防抖 */,\r\n debug = false /* 调试模式 */,\r\n syncTabs: defaultSyncTabs = false /* 默认不跨标签页同步 */,\r\n defaultTTL /* 默认 TTL */,\r\n } = options\r\n\r\n /* 清理函数映射表(key → cleanup) */\r\n const cleanupMap = new Map<StoreGeneric, () => void>()\r\n let isDisposed = false\r\n\r\n /**\r\n * 全局 dispose 函数\r\n * 清理所有 Store 的持久化订阅\r\n */\r\n const dispose = (): void => {\r\n if (isDisposed) {\r\n return\r\n }\r\n\r\n isDisposed = true\r\n cleanupMap.forEach(cleanup => cleanup())\r\n cleanupMap.clear()\r\n activePersistControllers.delete(dispose)\r\n }\r\n\r\n /* 注册到活跃控制器集合 */\r\n activePersistControllers.add(dispose)\r\n\r\n /**\r\n * Pinia 插件函数\r\n * 每当创建/使用一个 Pinia Store 时自动调用\r\n */\r\n const plugin: PiniaPlugin = (context: PiniaPluginContext): void => {\r\n if (isDisposed) {\r\n return\r\n }\r\n\r\n const { store, options: storeOptions } = context\r\n /* 规范化该 Store 的持久化配置 */\r\n const config = normalizePersistConfig(\r\n (\r\n storeOptions as {\r\n persist?: boolean | PersistOptions | EnhancedPersistOptions\r\n }\r\n ).persist\r\n )\r\n\r\n /* 该 Store 未配置持久化则跳过 */\r\n if (!config) {\r\n return\r\n }\r\n\r\n /* 解析该 Store 的存储配置(Store 级别配置优先于全局配置) */\r\n const storageType = config.storage ?? defaultStorage\r\n const storage = getStorage(storageType)\r\n const serializer =\r\n config.serializer?.serialize ?? JSON.stringify /* 序列化函数 */\r\n const deserialize =\r\n config.serializer?.deserialize ?? JSON.parse /* 反序列化函数 */\r\n const syncTabs = config.syncTabs ?? defaultSyncTabs /* 跨标签页同步 */\r\n const ttl = config.ttl ?? defaultTTL /* 过期时间 */\r\n const key = `${keyPrefix}${config.key || store.$id}` /* 存储键名 */\r\n\r\n /* 存储不可用则跳过 */\r\n if (!storage) {\r\n if (debug) {\r\n console.warn(`[PersistPlugin] Storage 不可用 (${store.$id})`)\r\n }\r\n return\r\n }\r\n\r\n /* 标记:是否正在从存储恢复状态(防止恢复过程中触发保存) */\r\n let isRestoring = false\r\n\r\n /**\r\n * 将持久化的原始状态字符串应用到 Store\r\n * 反序列化 → beforeRestore 钩子 → 恢复状态 → afterRestore 钩子\r\n */\r\n const applyPersistedValue = (rawState: string): void => {\r\n const nextState = deserialize(rawState) as Record<string, unknown>\r\n isRestoring = true /* 标记恢复中 */\r\n try {\r\n config.beforeRestore?.({ store }) /* 恢复前钩子 */\r\n restoreStoreState(store, nextState) /* 恢复状态 */\r\n config.afterRestore?.({ store }) /* 恢复后钩子 */\r\n } finally {\r\n isRestoring = false /* 恢复结束 */\r\n }\r\n }\r\n\r\n /**\r\n * 从存储恢复 Store 状态\r\n * 检查过期 → 反序列化 → 应用到 Store\r\n */\r\n const restoreState = (): void => {\r\n try {\r\n const stored = storage.getItem(key)\r\n if (!stored) {\r\n return /* 无持久化数据 */\r\n }\r\n\r\n const storageItem = JSON.parse(stored) as PersistedStorageItem\r\n /* 检查是否过期 */\r\n if (storageItem.expiresAt && Date.now() > storageItem.expiresAt) {\r\n storage.removeItem(key) /* 过期则清除 */\r\n return\r\n }\r\n\r\n /* 应用持久化值 */\r\n applyPersistedValue(storageItem.state)\r\n\r\n if (debug) {\r\n console.info(`[PersistPlugin] 已恢复状态 (${store.$id})`)\r\n }\r\n } catch (error) {\r\n console.error(`[PersistPlugin] 恢复状态失败 (${store.$id}):`, error)\r\n }\r\n }\r\n\r\n /**\r\n * 将当前 Store 状态持久化到存储\r\n * 过滤状态 → 序列化 → 包装为存储项 → 写入存储\r\n */\r\n const persistState = (): void => {\r\n /* 正在恢复时跳过保存,避免循环 */\r\n if (isRestoring) {\r\n return\r\n }\r\n\r\n try {\r\n /* 根据 include/exclude 路径过滤状态 */\r\n const filteredState = filterState(\r\n store.$state as Record<string, unknown>,\r\n config.paths,\r\n config.excludePaths\r\n )\r\n\r\n /* 构建存储项 */\r\n const payload: PersistedStorageItem = {\r\n state: serializer(filteredState) /* 序列化过滤后的状态 */,\r\n timestamp: Date.now() /* 保存时间 */,\r\n expiresAt: ttl ? Date.now() + ttl : undefined /* 过期时间 */,\r\n version: 1 /* 数据版本号 */,\r\n }\r\n\r\n /* 写入存储 */\r\n storage.setItem(key, JSON.stringify(payload))\r\n\r\n if (debug) {\r\n console.info(`[PersistPlugin] 已保存状态 (${store.$id})`)\r\n }\r\n } catch (error) {\r\n console.error(`[PersistPlugin] 保存状态失败 (${store.$id}):`, error)\r\n }\r\n }\r\n\r\n /* 创建防抖版的保存函数 */\r\n const saveState = debounce(persistState, config.debounce ?? debounceDelay)\r\n\r\n /* 订阅 Store 状态变化,自动触发保存 */\r\n const stopSubscribe = store.$subscribe(() => {\r\n /* 恢复中不保存 */\r\n if (!isRestoring) {\r\n saveState()\r\n }\r\n })\r\n\r\n /* ── 跨标签页同步 ── */\r\n let stopStorageSync = (): void => {} /* 清理函数占位 */\r\n if (syncTabs && typeof window !== 'undefined') {\r\n /* 监听 storage 事件,同步其他标签页的状态变化 */\r\n const handleStorage = (event: StorageEvent): void => {\r\n /* 只关注目标 key 且有新值 */\r\n if (event.key !== key) {\r\n return\r\n }\r\n\r\n if (event.newValue === null) {\r\n isRestoring = true\r\n try {\r\n store.$reset?.()\r\n } finally {\r\n isRestoring = false\r\n }\r\n return\r\n }\r\n\r\n try {\r\n const storageItem = JSON.parse(event.newValue) as PersistedStorageItem\r\n /* 跳过过期数据 */\r\n if (storageItem.expiresAt && Date.now() > storageItem.expiresAt) {\r\n return\r\n }\r\n\r\n /* 应用其他标签页的状态 */\r\n applyPersistedValue(storageItem.state)\r\n } catch (error) {\r\n console.error(`[PersistPlugin] 同步状态失败 (${store.$id}):`, error)\r\n }\r\n }\r\n\r\n window.addEventListener('storage', handleStorage)\r\n /* 保存清理函数 */\r\n stopStorageSync = () => {\r\n window.removeEventListener('storage', handleStorage)\r\n }\r\n }\r\n\r\n /**\r\n * 该 Store 的清理函数\r\n * 取消防抖、停止订阅、移除事件监听\r\n */\r\n const cleanup = (): void => {\r\n saveState.cancel() /* 取消待执行的防抖保存 */\r\n stopSubscribe() /* 取消状态订阅 */\r\n stopStorageSync() /* 移除跨标签页同步监听 */\r\n cleanupMap.delete(store)\r\n store[STORE_PERSIST_DISPOSE_KEY]?.() /* 从清理映射中移除 */\r\n }\r\n\r\n /* 注册清理函数 */\r\n cleanupMap.set(store, cleanup)\r\n /* 为 Store 的 $dispose 打补丁,确保销毁时自动清理 */\r\n patchPersistDispose(store as DisposableStore, cleanup)\r\n /* 立即尝试从存储恢复状态 */\r\n restoreState()\r\n }\r\n\r\n return {\r\n plugin /* Pinia 插件函数 */,\r\n dispose /* 全局清理函数 */,\r\n }\r\n}\r\n\r\n/**\r\n * 创建基础版持久化插件\r\n * @param options - 插件配置\r\n * @returns Pinia 插件函数\r\n */\r\nexport function createPersistPlugin(\r\n options: PersistPluginOptions = {}\r\n): PiniaPlugin {\r\n return createPersistPluginController(options).plugin\r\n}\r\n\r\n/**\r\n * 创建增强版持久化插件\r\n * 与基础版功能相同,类型签名保留扩展空间\r\n * @param options - 插件配置\r\n * @returns Pinia 插件函数\r\n */\r\nexport function createEnhancedPersistPlugin(\r\n options: EnhancedPersistPluginOptions = {}\r\n): PiniaPlugin {\r\n return createPersistPluginController(options).plugin\r\n}\r\n\r\n/**\r\n * 全局清理所有活跃的持久化控制器\r\n * 通常在应用卸载或测试中调用\r\n */\r\nexport function cleanupEnhancedPersist(): void {\r\n Array.from(activePersistControllers).forEach(dispose => dispose())\r\n}\r\n","// 从 pinia 导入 Pinia 类型\r\nimport type { Pinia } from 'pinia'\r\n// 导入增强持久化插件选项类型\r\nimport type { EnhancedPersistPluginOptions } from '../plugins/pinia-plugin'\r\n\r\n/**\r\n * store 运行时配置选项接口\r\n * 用于配置 store 运行时的行为\r\n */\r\nexport interface StoreRuntimeOptions {\r\n // 可选的 Pinia 实例,如果不提供则自动创建\r\n pinia?: Pinia\r\n // 是否启用持久化功能\r\n persist?: boolean\r\n // 持久化插件的详细配置选项\r\n persistOptions?: EnhancedPersistPluginOptions\r\n // 是否将 store API 注册到 Vue 全局属性\r\n globalProperties?: boolean\r\n // 是否通过 provide/inject 提供 store 实例\r\n provide?: boolean\r\n // 是否开启调试模式\r\n debug?: boolean\r\n}\r\n\r\n/**\r\n * store Vue 插件选项接口\r\n * 继承自 StoreRuntimeOptions,当前无额外扩展\r\n */\r\nexport interface StoreVuePluginOptions extends StoreRuntimeOptions {}\r\n\r\n/**\r\n * 解析后的 store 运行时选项接口\r\n * 所有字段均已填充默认值,不再包含可选标记\r\n */\r\nexport interface ResolvedStoreRuntimeOptions {\r\n // Pinia 实例\r\n pinia?: Pinia\r\n // 是否启用持久化\r\n persist: boolean\r\n // 持久化插件配置\r\n persistOptions: EnhancedPersistPluginOptions\r\n // 是否注册全局属性\r\n globalProperties: boolean\r\n // 是否通过 provide 提供\r\n provide: boolean\r\n // 是否开启调试\r\n debug: boolean\r\n}\r\n\r\n/**\r\n * 将用户传入的 store 运行时选项规范化为完整配置\r\n * 为每个可选字段填充默认值\r\n * @param options - 用户传入的配置选项\r\n * @returns 规范化后的完整配置对象\r\n */\r\nexport function normalizeStoreRuntimeOptions(\r\n options: StoreRuntimeOptions = {}\r\n): ResolvedStoreRuntimeOptions {\r\n return {\r\n pinia: options.pinia, // 透传 Pinia 实例\r\n persist: options.persist ?? false, // 默认不启用持久化\r\n persistOptions: options.persistOptions ?? {}, // 默认空持久化配置\r\n globalProperties: options.globalProperties ?? true, // 默认注册全局属性\r\n provide: options.provide ?? true, // 默认通过 provide 提供\r\n debug: options.debug ?? false, // 默认关闭调试\r\n }\r\n}\r\n","/* ============================================================\r\n * store-registry.ts\r\n * Store 注册表模块\r\n * 管理所有活跃的 Pinia Store 实例,提供注册/注销/查询/重置能力\r\n * 通过 patch $dispose 方法实现 Store 销毁时自动注销\r\n * ============================================================ */\r\n\r\n/* 从 Pinia 导入类型 */\r\nimport type { Pinia, PiniaPluginContext, StoreGeneric } from 'pinia'\r\n/* 导入全局 Store API 类型 */\r\nimport type { GlobalStoreAPI } from '../types'\r\n\r\n/* 用于标记 store 的 $dispose 是否已被 patch 的 Symbol 键 */\r\nconst STORE_DISPOSE_PATCH_KEY = Symbol('ldesign-store-registry-dispose')\r\n\r\n/* 可被 patch $dispose 的 Store 扩展类型 */\r\ntype DisposableStore = StoreGeneric & {\r\n [STORE_DISPOSE_PATCH_KEY]?: () => void /* 恢复原始 $dispose 的函数 */\r\n}\r\n\r\n/* 扩展 Pinia 类型,访问内部的 _s(Store 实例映射) */\r\ntype PiniaWithStores = Pinia & {\r\n _s?: Map<string, StoreGeneric>\r\n}\r\n\r\n/**\r\n * Store 注册表接口\r\n * 提供对所有活跃 Store 的集中管理能力\r\n * @property stores - Store 实例映射(storeId → Store)\r\n * @property register - 注册一个 Store\r\n * @property unregister - 注销一个 Store\r\n * @property hasStore - 检查 Store 是否已注册\r\n * @property getStore - 获取指定 ID 的 Store\r\n * @property getStoreIds - 获取所有已注册的 Store ID\r\n * @property resetAll - 重置所有 Store 到初始状态\r\n * @property clear - 清空注册表(不重置 Store)\r\n * @property registerExistingStores - 从 Pinia 实例中批量注册已有的 Store\r\n * @property createStoreAPI - 创建全局 Store API 对象\r\n */\r\nexport interface StoreRegistry {\r\n readonly stores: Map<string, StoreGeneric>\r\n register: (store: StoreGeneric) => void\r\n unregister: (storeId: string) => void\r\n hasStore: (storeId: string) => boolean\r\n getStore: <T = unknown>(storeId: string) => T | undefined\r\n getStoreIds: () => string[]\r\n resetAll: () => void\r\n clear: () => void\r\n registerExistingStores: (pinia: Pinia) => void\r\n createStoreAPI: (pinia: Pinia) => GlobalStoreAPI\r\n}\r\n\r\n/**\r\n * 为 Store 的 $dispose 方法打补丁\r\n * 在 Store 被销毁时自动从注册表中注销\r\n * 确保注册表中的 Store 引用不会过期\r\n *\r\n * @param store - 目标 Store\r\n * @param unregister - 注销回调函数\r\n */\r\nfunction patchStoreDispose(\r\n store: DisposableStore,\r\n unregister: () => void\r\n): void {\r\n /* 如果之前已有补丁,先恢复原始方法 */\r\n const restorePatchedDispose = store[STORE_DISPOSE_PATCH_KEY]\r\n if (restorePatchedDispose) {\r\n restorePatchedDispose()\r\n }\r\n\r\n /* 保存原始 $dispose 方法 */\r\n const originalDispose = store.$dispose\r\n /* 替换为带注销逻辑的版本 */\r\n store.$dispose = function patchedDispose() {\r\n unregister()\r\n return originalDispose.call(this) /* 调用原始 dispose */\r\n }\r\n\r\n /* 保存恢复函数,供后续补丁使用 */\r\n store[STORE_DISPOSE_PATCH_KEY] = () => {\r\n store.$dispose = originalDispose\r\n delete store[STORE_DISPOSE_PATCH_KEY]\r\n }\r\n}\r\n\r\n/**\r\n * 创建 Store 注册表实例\r\n * @returns StoreRegistry 注册表对象\r\n */\r\nexport function createStoreRegistry(): StoreRegistry {\r\n /* Store 实例映射(storeId → Store) */\r\n const stores = new Map<string, StoreGeneric>()\r\n\r\n /**\r\n * 注销指定 ID 的 Store\r\n * @param storeId - Store ID\r\n */\r\n const unregister = (storeId: string, expectedStore?: StoreGeneric): void => {\r\n const store = stores.get(storeId)\r\n if (!store || (expectedStore && store !== expectedStore)) {\r\n return\r\n }\r\n\r\n stores.delete(storeId)\r\n ;(store as DisposableStore)[STORE_DISPOSE_PATCH_KEY]?.()\r\n }\r\n\r\n /**\r\n * 注册一个 Store\r\n * 同时为 $dispose 打补丁,确保销毁时自动注销\r\n * @param store - 要注册的 Store 实例\r\n */\r\n const register = (store: StoreGeneric): void => {\r\n const existingStore = stores.get(store.$id)\r\n if (existingStore && existingStore !== store) {\r\n unregister(store.$id, existingStore)\r\n }\r\n\r\n stores.set(store.$id, store)\r\n patchStoreDispose(store as DisposableStore, () =>\r\n unregister(store.$id, store)\r\n )\r\n }\r\n\r\n /**\r\n * 检查 Store 是否已注册\r\n * @param storeId - Store ID\r\n * @returns 是否已注册\r\n */\r\n const hasStore = (storeId: string): boolean => {\r\n return stores.has(storeId)\r\n }\r\n\r\n /**\r\n * 获取指定 ID 的 Store\r\n * @param storeId - Store ID\r\n * @returns Store 实例或 undefined\r\n */\r\n const getStore = <T = unknown>(storeId: string): T | undefined => {\r\n return stores.get(storeId) as T | undefined\r\n }\r\n\r\n /**\r\n * 获取所有已注册的 Store ID 列表\r\n * @returns Store ID 数组\r\n */\r\n const getStoreIds = (): string[] => {\r\n return Array.from(stores.keys())\r\n }\r\n\r\n /**\r\n * 重置所有 Store 到初始状态\r\n * 调用每个 Store 的 $reset() 方法\r\n * 注意:Setup stores 可能不支持 $reset\r\n */\r\n const resetAll = (): void => {\r\n stores.forEach(store => {\r\n /* 检查是否有 $reset 方法 */\r\n if (typeof store.$reset !== 'function') {\r\n return\r\n }\r\n\r\n try {\r\n store.$reset()\r\n } catch {\r\n /* Setup stores 可能暴露了 $reset 但实际拒绝直接重置 */\r\n }\r\n })\r\n }\r\n\r\n /**\r\n * 清空注册表\r\n * 只清除映射,不重置 Store\r\n */\r\n const clear = (): void => {\r\n stores.forEach(store => {\r\n ;(store as DisposableStore)[STORE_DISPOSE_PATCH_KEY]?.()\r\n })\r\n stores.clear()\r\n }\r\n\r\n /**\r\n * 从 Pinia 实例中批量注册已有的 Store\r\n * 访问 Pinia 内部的 _s 映射(所有已创建的 Store)\r\n * @param pinia - Pinia 实例\r\n */\r\n const registerExistingStores = (pinia: Pinia): void => {\r\n const typedPinia = pinia as PiniaWithStores\r\n typedPinia._s?.forEach(store => register(store))\r\n }\r\n\r\n /**\r\n * 创建全局 Store API 对象\r\n * 提供只读的 Store 查询和管理能力\r\n * @param pinia - Pinia 实例\r\n * @returns GlobalStoreAPI 对象\r\n */\r\n const createStoreAPI = (pinia: Pinia): GlobalStoreAPI => {\r\n return {\r\n pinia /* Pinia 实例引用 */,\r\n getStore /* 获取 Store */,\r\n hasStore /* 检查 Store 是否存在 */,\r\n getStoreIds /* 获取所有 Store ID */,\r\n resetAll /* 重置所有 Store */,\r\n }\r\n }\r\n\r\n /* 返回完整的注册表对象 */\r\n return {\r\n stores,\r\n register,\r\n unregister,\r\n hasStore,\r\n getStore,\r\n getStoreIds,\r\n resetAll,\r\n clear,\r\n registerExistingStores,\r\n createStoreAPI,\r\n }\r\n}\r\n\r\n/* ── 全局注册表单例 ── */\r\n\r\n/* 全局注册表实例(模块级单例) */\r\nlet globalRegistry = createStoreRegistry()\r\n\r\n/**\r\n * 设置全局注册表\r\n * 传 null 则创建新的空注册表\r\n * @param registry - 新的注册表或 null\r\n */\r\nexport function setGlobalStoreRegistry(registry: StoreRegistry | null): void {\r\n if (registry === globalRegistry) {\r\n return\r\n }\r\n\r\n globalRegistry.clear()\r\n globalRegistry = registry ?? createStoreRegistry()\r\n}\r\n\r\n/**\r\n * 获取全局 Store 映射(兼容旧接口)\r\n * @returns Store 映射(类型擦除版)\r\n */\r\nexport function getStoreRegistry(): Map<string, unknown> {\r\n return globalRegistry.stores as Map<string, unknown>\r\n}\r\n\r\n/**\r\n * 创建注册表方法集合\r\n * 从注册表中提取常用的查询方法\r\n * @param registry - 注册表实例,默认使用全局注册表\r\n * @returns 方法集合对象\r\n */\r\nexport function createRegistryMethods(\r\n registry: StoreRegistry = globalRegistry\r\n) {\r\n return {\r\n getStore: registry.getStore,\r\n hasStore: registry.hasStore,\r\n getStoreIds: registry.getStoreIds,\r\n resetAll: registry.resetAll,\r\n }\r\n}\r\n\r\n/**\r\n * 创建注册表 Pinia 插件\r\n * 当 Store 被创建/使用时自动注册到注册表\r\n * @param registry - 注册表实例,默认使用全局注册表\r\n * @returns Pinia 插件函数\r\n */\r\nexport function createStoreRegistryPlugin(\r\n registry: StoreRegistry = globalRegistry\r\n) {\r\n return ({ store }: PiniaPluginContext): void => {\r\n registry.register(store)\r\n }\r\n}\r\n","/* ============================================================\r\n * store-runtime.ts\r\n * Store 运行时模块\r\n * 负责创建和管理 Store 运行时实例,包括 Pinia 创建、\r\n * 注册表初始化、持久化插件配置和生命周期管理\r\n * ============================================================ */\r\n\r\n/* 从 Pinia 导入类型 */\r\nimport type { Pinia, StoreGeneric } from 'pinia'\r\n/* 全局 Store API 类型 */\r\nimport type { GlobalStoreAPI } from '../types'\r\n/* 运行时选项类型 */\r\nimport type {\r\n ResolvedStoreRuntimeOptions /* 解析后的运行时选项 */,\r\n StoreRuntimeOptions /* 原始运行时选项 */,\r\n} from './store-plugin-options'\r\n/* 注册表类型 */\r\nimport type { StoreRegistry } from './store-registry'\r\n/* 从 Pinia 导入创建实例函数 */\r\nimport { createPinia } from 'pinia'\r\n/* 导入持久化插件控制器 */\r\nimport { createPersistPluginController } from '../plugins/pinia-plugin'\r\n/* 导入选项规范化函数 */\r\nimport { normalizeStoreRuntimeOptions } from './store-plugin-options'\r\n/* 导入注册表相关函数 */\r\nimport {\r\n createStoreRegistry,\r\n createStoreRegistryPlugin,\r\n setGlobalStoreRegistry,\r\n} from './store-registry'\r\n\r\n/* 用于标记 Pinia 实例上已绑定的运行时的 Symbol 键 */\r\nconst STORE_RUNTIME_KEY = Symbol('ldesign-store-runtime')\r\n\r\n/* 扩展 Pinia 类型,添加运行时和 Store 映射引用 */\r\ntype PiniaWithRuntime = Pinia & {\r\n _s?: Map<string, StoreGeneric> /* Pinia 内部的 Store 实例映射 */\r\n [STORE_RUNTIME_KEY]?: StoreRuntime /* 绑定的运行时 */\r\n}\r\n\r\n/**\r\n * Store 运行时接口\r\n * 封装了 Pinia、注册表、API 和生命周期管理\r\n * @property pinia - Pinia 实例\r\n * @property registry - Store 注册表\r\n * @property storeAPI - 全局 Store API\r\n * @property options - 解析后的运行时选项\r\n * @property dispose - 清理函数,释放所有资源\r\n */\r\nexport interface StoreRuntime {\r\n pinia: Pinia\r\n registry: StoreRegistry\r\n storeAPI: GlobalStoreAPI\r\n options: ResolvedStoreRuntimeOptions\r\n dispose: () => void\r\n}\r\n\r\n/* 全局运行时实例(模块级单例) */\r\nlet globalStoreRuntime: StoreRuntime | null = null\r\n\r\n/**\r\n * 获取全局 Store API\r\n * @returns 全局 Store API 或 null(运行时未初始化时)\r\n */\r\nexport function getGlobalStoreAPI(): GlobalStoreAPI | null {\r\n return globalStoreRuntime?.storeAPI ?? null\r\n}\r\n\r\n/**\r\n * 设置全局运行时\r\n * 同步更新全局注册表引用\r\n * @param runtime - 新的运行时实例或 null\r\n */\r\nexport function setGlobalStoreRuntime(runtime: StoreRuntime | null): void {\r\n globalStoreRuntime = runtime\r\n /* 同步更新全局注册表 */\r\n setGlobalStoreRegistry(runtime?.registry ?? null)\r\n}\r\n\r\n/**\r\n * 创建 Store 运行时实例\r\n * 这是 Store 系统的核心工厂函数,负责:\r\n * 1. 规范化配置选项\r\n * 2. 创建或复用 Pinia 实例\r\n * 3. 创建 Store 注册表\r\n * 4. 配置持久化插件\r\n * 5. 注册 Pinia 插件\r\n * 6. 组装运行时对象\r\n *\r\n * @param options - 运行时配置选项\r\n * @returns StoreRuntime 实例\r\n */\r\nexport function createStoreRuntime(\r\n options: StoreRuntimeOptions = {}\r\n): StoreRuntime {\r\n /* 规范化配置选项(填充默认值) */\r\n const resolvedOptions = normalizeStoreRuntimeOptions(options)\r\n /* 创建或复用 Pinia 实例,扩展为带运行时标记的类型 */\r\n const pinia = (resolvedOptions.pinia ?? createPinia()) as PiniaWithRuntime\r\n\r\n /* 如果 Pinia 实例上已有运行时,直接复用(防止重复创建) */\r\n if (pinia[STORE_RUNTIME_KEY]) {\r\n return pinia[STORE_RUNTIME_KEY]\r\n }\r\n\r\n /* 创建 Store 注册表 */\r\n const registry = createStoreRegistry()\r\n /* Pinia 不提供插件移除 API,因此释放后必须让已安装插件失效。 */\r\n let isActive = true\r\n\r\n /* 如果启用了持久化,创建持久化插件控制器 */\r\n const persistController = resolvedOptions.persist\r\n ? createPersistPluginController({\r\n ...resolvedOptions.persistOptions /* 透传持久化选项 */,\r\n debug:\r\n resolvedOptions.debug ||\r\n resolvedOptions.persistOptions.debug /* 调试模式 */,\r\n })\r\n : null\r\n\r\n /* 注册注册表插件(自动注册新创建的 Store) */\r\n const registryPlugin = createStoreRegistryPlugin(registry)\r\n pinia.use(context => {\r\n if (isActive) {\r\n registryPlugin(context)\r\n }\r\n })\r\n /* 如果有持久化插件,也注册到 Pinia */\r\n persistController && pinia.use(persistController.plugin)\r\n /* 批量注册 Pinia 中已有的 Store */\r\n registry.registerExistingStores(pinia)\r\n\r\n /* 组装运行时对象 */\r\n const runtime: StoreRuntime = {\r\n pinia /* Pinia 实例 */,\r\n registry /* Store 注册表 */,\r\n storeAPI: registry.createStoreAPI(pinia) /* 全局 Store API */,\r\n options: resolvedOptions /* 解析后的选项 */,\r\n\r\n /**\r\n * 清理函数\r\n * 释放所有资源:清除运行时标记、持久化插件、注册表\r\n */\r\n dispose: () => {\r\n if (!isActive) {\r\n return\r\n }\r\n\r\n isActive = false\r\n\r\n /* 清除 Pinia 上的运行时标记 */\r\n if (pinia[STORE_RUNTIME_KEY] === runtime) {\r\n delete pinia[STORE_RUNTIME_KEY]\r\n }\r\n\r\n /* 销毁持久化插件(取消订阅、移除事件监听等) */\r\n persistController?.dispose()\r\n /* 清空注册表 */\r\n registry.clear()\r\n\r\n /* 如果当前是全局运行时,也清除全局引用 */\r\n if (globalStoreRuntime === runtime) {\r\n setGlobalStoreRuntime(null)\r\n }\r\n },\r\n }\r\n\r\n /* 在 Pinia 实例上保存运行时引用(防止重复创建) */\r\n pinia[STORE_RUNTIME_KEY] = runtime\r\n return runtime\r\n}\r\n","/* ============================================================\r\n * install-store-app.ts\r\n * Store 运行时安装到 Vue App 的模块\r\n * 负责将 StoreRuntime 安装到 Vue 应用实例,管理 Pinia 注入、\r\n * provide/globalProperties 注入和 unmount 生命周期清理\r\n * ============================================================ */\r\n\r\n/* Vue App 类型 */\r\nimport type { App } from 'vue'\r\n/* Store 运行时类型 */\r\nimport type { StoreRuntime } from './store-runtime'\r\n/* 从注入键模块导入 Symbol 常量 */\r\nimport {\r\n STORE_API_SYMBOL /* Store API 的注入键 */,\r\n STORE_PINIA_SYMBOL /* Pinia 实例的注入键 */,\r\n STORE_RUNTIME_SYMBOL /* 运行时的注入键 */,\r\n} from '../context/injection-keys'\r\n/* 导入全局运行时设置函数 */\r\nimport { setGlobalStoreRuntime } from './store-runtime'\r\n\r\n/* 用于标记 App 上已安装的运行时的 Symbol 键 */\r\nconst STORE_APP_RUNTIME_KEY = Symbol('ldesign-store-app-runtime')\r\n\r\n/* 扩展 Vue App 类型,添加运行时引用 */\r\ntype StoreApp = App & {\r\n [STORE_APP_RUNTIME_KEY]?: StoreRuntime\r\n}\r\n\r\n/**\r\n * 检查 Pinia 是否已安装到 Vue App\r\n * 通过比较 app.config.globalProperties.$pinia 与 runtime 的 pinia 实例\r\n *\r\n * @param app - Vue 应用实例\r\n * @param runtime - Store 运行时\r\n * @returns Pinia 是否已安装\r\n */\r\nfunction isPiniaInstalled(app: App, runtime: StoreRuntime): boolean {\r\n return app.config.globalProperties.$pinia === runtime.pinia\r\n}\r\n\r\n/**\r\n * 为 Vue App 的 unmount 方法打补丁\r\n * 在应用卸载时自动清理 Store 运行时(dispose)\r\n * 确保资源不会泄漏\r\n *\r\n * @param app - Vue 应用实例\r\n * @param runtime - Store 运行时\r\n */\r\nfunction patchUnmount(app: StoreApp, runtime: StoreRuntime): void {\r\n /* 保存原始 unmount 方法 */\r\n const originalUnmount = app.unmount\r\n\r\n /* 替换为带清理逻辑的版本 */\r\n app.unmount = function patchedUnmount() {\r\n /* 检查当前运行时是否仍是这个 runtime(防止被后续安装覆盖) */\r\n const activeRuntime = app[STORE_APP_RUNTIME_KEY]\r\n if (activeRuntime === runtime) {\r\n /* 清除 App 上的运行时引用 */\r\n delete app[STORE_APP_RUNTIME_KEY]\r\n /* 执行运行时的 dispose(清理持久化、注册表等) */\r\n runtime.dispose()\r\n }\r\n\r\n /* 调用原始 unmount 方法 */\r\n return originalUnmount.call(this)\r\n }\r\n}\r\n\r\n/**\r\n * 获取 Vue App 上已安装的 Store 运行时\r\n * @param app - Vue 应用实例\r\n * @returns 已安装的运行时或 undefined\r\n */\r\nexport function getInstalledStoreRuntime(app: App): StoreRuntime | undefined {\r\n return (app as StoreApp)[STORE_APP_RUNTIME_KEY]\r\n}\r\n\r\n/**\r\n * 将 Store 运行时安装到 Vue App\r\n * 这是 Vue 适配层的核心安装入口,负责:\r\n * 1. 安装 Pinia 插件\r\n * 2. 通过 provide 注入 Store 能力\r\n * 3. 挂载到 globalProperties\r\n * 4. 打 unmount 补丁进行生命周期管理\r\n * 5. 设置全局运行时引用\r\n *\r\n * @param app - Vue 应用实例\r\n * @param runtime - Store 运行时实例\r\n * @returns 安装的运行时(如果已安装则返回已有的)\r\n */\r\nexport function installStoreRuntime(\r\n app: App,\r\n runtime: StoreRuntime\r\n): StoreRuntime {\r\n const storeApp = app as StoreApp\r\n /* 检查是否已安装(幂等性) */\r\n const installedRuntime = storeApp[STORE_APP_RUNTIME_KEY]\r\n\r\n if (installedRuntime) {\r\n /* 已安装则设置全局引用并返回已有的运行时 */\r\n setGlobalStoreRuntime(installedRuntime)\r\n return installedRuntime\r\n }\r\n\r\n /* 如果 Pinia 尚未安装,使用 app.use() 安装 */\r\n if (!isPiniaInstalled(app, runtime)) {\r\n app.use(runtime.pinia)\r\n }\r\n\r\n /* 在 App 上保存运行时引用(标记已安装) */\r\n storeApp[STORE_APP_RUNTIME_KEY] = runtime\r\n\r\n /* 如果配置了 provide 注入,通过 Vue 的 provide/inject 注入 Store 能力 */\r\n if (runtime.options.provide) {\r\n app.provide(STORE_RUNTIME_SYMBOL, runtime) /* 注入运行时 */\r\n app.provide(STORE_API_SYMBOL, runtime.storeAPI) /* 注入 Store API */\r\n app.provide(STORE_PINIA_SYMBOL, runtime.pinia) /* 注入 Pinia 实例 */\r\n app.provide('store', runtime.storeAPI) /* 简写键 'store' */\r\n app.provide('pinia', runtime.pinia) /* 简写键 'pinia' */\r\n }\r\n\r\n /* 如果配置了 globalProperties,挂载到全局属性上 */\r\n if (runtime.options.globalProperties) {\r\n app.config.globalProperties.$store = runtime.storeAPI\r\n }\r\n\r\n /* 打 unmount 补丁,确保卸载时自动清理 */\r\n patchUnmount(storeApp, runtime)\r\n /* 设置为全局运行时引用 */\r\n setGlobalStoreRuntime(runtime)\r\n\r\n return runtime\r\n}\r\n","/* ============================================================\r\n * engine-plugin.ts\r\n * Store Engine 插件(Vue 适配层)\r\n * 将 core 层的 createStoreEngineBridge 包装为 Vue 友好的插件接口\r\n * 负责创建 bridge、管理运行时生命周期、连接 Vue App\r\n * ============================================================ */\r\n\r\n/* 从 core/engine 导入 bridge 相关类型 */\r\nimport type {\r\n StoreEngineBridge /* 引擎桥接实例接口 */,\r\n StoreEngineLike /* 引擎抽象接口 */,\r\n StoreEnginePluginContextLike /* 插件上下文接口 */,\r\n} from '@composy/store-core/engine'\r\n/* Vue App 类型 */\r\nimport type { App } from 'vue'\r\n/* 本地 store 运行时类型 */\r\nimport type { StoreRuntime } from '../runtime/store-runtime'\r\n/* 全局 Store API 和插件选项类型 */\r\nimport type { GlobalStoreAPI, StoreEnginePluginOptions } from '../types'\r\n/* 从 core/engine 导入 bridge 创建函数 */\r\nimport { createStoreEngineBridge } from '@composy/store-core/engine'\r\n/* 导入 Vue App 运行时安装相关函数 */\r\nimport {\r\n getInstalledStoreRuntime,\r\n installStoreRuntime,\r\n} from '../runtime/install-store-app'\r\n/* 导入运行时创建函数和全局 API 获取函数 */\r\nimport {\r\n createStoreRuntime,\r\n getGlobalStoreAPI as getActiveStoreAPI,\r\n} from '../runtime/store-runtime'\r\n\r\n/**\r\n * Store Engine 插件接口\r\n * 定义了插件的元信息和生命周期方法\r\n * @property dependencies - 依赖的其他插件名称列表\r\n * @property name - 插件名称\r\n * @property version - 插件版本\r\n * @property install - 安装到引擎上下文(异步)\r\n * @property installVue - 安装到 Vue App\r\n * @property uninstall - 从引擎上下文卸载(异步)\r\n */\r\nexport interface StoreEnginePlugin {\r\n dependencies: string[]\r\n name: string\r\n version: string\r\n install: (context: StoreEnginePluginContextLike) => Promise<void>\r\n installVue: (app: App) => void\r\n uninstall: (context?: StoreEnginePluginContextLike) => Promise<void>\r\n}\r\n\r\n/**\r\n * 类型守卫:判断值是否为 Vue App 实例\r\n * 通过检查是否有 use 方法来识别\r\n */\r\nfunction isVueApp(value: unknown): value is App {\r\n return !!value && typeof (value as App).use === 'function'\r\n}\r\n\r\n/**\r\n * 从各种可能的来源解析 Vue App 实例\r\n * 按优先级尝试:payload.app → payload.framework.app → engine.getApp() → context.framework.app\r\n *\r\n * @param engine - Store 引擎实例\r\n * @param context - 插件上下文\r\n * @param payload - 安装时传入的额外数据\r\n * @returns 解析到的 Vue App 或 undefined\r\n */\r\nfunction resolveVueApp(\r\n engine: StoreEngineLike,\r\n context: StoreEnginePluginContextLike,\r\n payload?: unknown\r\n): App | undefined {\r\n /* 将 payload 宽松转为可能包含 app 字段的对象 */\r\n const appPayload = payload as\r\n { app?: unknown; framework?: { app?: unknown } } | undefined\r\n /* 按优先级尝试多个来源获取 app */\r\n const app =\r\n appPayload?.app ??\r\n appPayload?.framework?.app ??\r\n engine.getApp?.() ??\r\n context.framework?.app\r\n\r\n /* 使用类型守卫验证是否为 Vue App */\r\n return isVueApp(app) ? app : undefined\r\n}\r\n\r\n/**\r\n * 获取当前活跃的全局 Store API\r\n * @returns 全局 Store API 或 null(未初始化时)\r\n */\r\nexport function getGlobalStoreAPI(): GlobalStoreAPI | null {\r\n return getActiveStoreAPI()\r\n}\r\n\r\n/**\r\n * 创建 Store Engine 插件工厂函数\r\n * 这是 Vue 适配层的核心入口,将 core 层的 bridge 包装为 Vue 友好的插件\r\n *\r\n * @param options - 插件配置选项\r\n * @returns StoreEnginePlugin 插件实例\r\n */\r\nexport function createStoreEnginePlugin(\r\n options: StoreEnginePluginOptions = {}\r\n): StoreEnginePlugin {\r\n /* 解构配置选项,设置默认值 */\r\n const {\r\n debug = false /* 是否开启调试日志 */,\r\n dependencies = [] /* 依赖的插件列表 */,\r\n name = 'store' /* 插件名称 */,\r\n version = '0.0.1' /* 插件版本 */,\r\n } = options\r\n\r\n /* 延迟创建的 bridge 实例引用 */\r\n let bridge: StoreEngineBridge | null = null\r\n\r\n /**\r\n * 创建 bridge 的工厂函数\r\n * 内部调用 core 层的 createStoreEngineBridge,并传入 Vue 特定的实现\r\n */\r\n const createBridge = (): StoreEngineBridge => {\r\n const activeBridge = createStoreEngineBridge<StoreRuntime>({\r\n name /* 插件名称 */,\r\n version /* 插件版本 */,\r\n debug /* 调试模式 */,\r\n\r\n /* 创建运行时:传入所有 Vue 相关配置 */\r\n createRuntime: () =>\r\n createStoreRuntime({\r\n pinia: options.pinia /* 可选的外部 Pinia 实例 */,\r\n persist: options.persist /* 是否启用持久化 */,\r\n persistOptions: options.persistOptions /* 持久化选项 */,\r\n globalProperties: options.globalProperties /* 是否挂载到全局属性 */,\r\n provide: options.provide /* 是否通过 provide 注入 */,\r\n debug,\r\n }),\r\n\r\n /* 将运行时安装到 Vue App */\r\n attachToApp: (app, runtime) => installStoreRuntime(app as App, runtime),\r\n\r\n /* 从 Vue App 获取已安装的运行时 */\r\n getRuntimeFromApp: app => getInstalledStoreRuntime(app as App),\r\n\r\n /* Vue App 类型守卫 */\r\n isApp: isVueApp,\r\n\r\n /* Vue App 解析函数 */\r\n resolveApp: resolveVueApp,\r\n })\r\n\r\n bridge = activeBridge\r\n return activeBridge\r\n }\r\n\r\n /**\r\n * 获取 bridge 实例(懒创建)\r\n * 首次调用时自动创建\r\n */\r\n const getBridge = (): StoreEngineBridge => {\r\n return bridge ?? createBridge()\r\n }\r\n\r\n /* ── 返回插件对象 ── */\r\n return {\r\n name /* 插件名称 */,\r\n version /* 插件版本 */,\r\n dependencies /* 插件依赖 */,\r\n\r\n /**\r\n * 安装到引擎上下文(异步)\r\n * 通过 bridge 将 store 能力注册到引擎\r\n */\r\n async install(context: StoreEnginePluginContextLike): Promise<void> {\r\n await getBridge().install(context)\r\n },\r\n\r\n /**\r\n * 安装到 Vue App\r\n * 将 store 运行时连接到 Vue 应用实例\r\n */\r\n installVue(app: App): void {\r\n getBridge().attachToApp(app)\r\n },\r\n\r\n /**\r\n * 从引擎上下文卸载(异步)\r\n * 清理 bridge 引用\r\n */\r\n async uninstall(context?: StoreEnginePluginContextLike): Promise<void> {\r\n await getBridge().uninstall(context)\r\n bridge = null /* 释放 bridge 引用,允许垃圾回收 */\r\n },\r\n }\r\n}\r\n\r\n/* 默认导出工厂函数 */\r\nexport default createStoreEnginePlugin\r\n","// 从 vue 导入 App 和 Plugin 类型\r\nimport type { App, Plugin } from 'vue'\r\n// 导入 store Vue 插件选项类型\r\nimport type { StoreVuePluginOptions } from '../runtime/store-plugin-options'\r\n// 导入安装 store 运行时的函数\r\nimport { installStoreRuntime } from '../runtime/install-store-app'\r\n// 导入创建 store 运行时的函数\r\nimport { createStoreRuntime } from '../runtime/store-runtime'\r\n\r\n// 重新导出 store Vue 插件选项类型\r\nexport type { StoreVuePluginOptions } from '../runtime/store-plugin-options'\r\n\r\n/**\r\n * 创建 store Vue 插件\r\n * @param options - store Vue 插件配置选项\r\n * @returns 返回 Vue Plugin 对象,可通过 app.use() 安装\r\n */\r\nexport function createStoreVuePlugin(\r\n options: StoreVuePluginOptions = {}\r\n): Plugin {\r\n return {\r\n // 安装插件到 Vue 应用\r\n install(app: App): void {\r\n // 创建 store 运行时实例\r\n const runtime = createStoreRuntime(options)\r\n // 将运行时安装到 Vue 应用,如果已安装则返回已有的运行时\r\n const installedRuntime = installStoreRuntime(app, runtime)\r\n\r\n // 如果安装的运行时不是新创建的,则销毁新建的运行时以避免内存泄漏\r\n if (installedRuntime !== runtime) {\r\n runtime.dispose()\r\n }\r\n\r\n // 如果开启了调试模式,输出安装成功日志\r\n if (installedRuntime.options.debug) {\r\n console.info('[StoreVuePlugin] installed')\r\n }\r\n },\r\n }\r\n}\r\n","/**\r\n * @composy/store-vue - Vue 3 状态管理适配器\r\n *\r\n * 基于 Pinia 的增强版状态管理解决方案\r\n *\r\n * 主要功能:\r\n * - Pinia 集成与增强\r\n * - 状态持久化\r\n * - Engine 插件集成\r\n * - 丰富的 Composables\r\n *\r\n * @module @composy/store-vue\r\n */\r\n\r\n// === Composables ===\r\nexport {\r\n createStoreGetters,\r\n createStoreState,\r\n // Setup Store 助手\r\n defineSetupStore,\r\n email,\r\n maxLength,\r\n minLength,\r\n numberRange,\r\n pattern,\r\n // 验证规则工具\r\n required,\r\n useComputedStore,\r\n useFormStore,\r\n useOnAction,\r\n useOptimistic,\r\n usePersist,\r\n usePersistedRef,\r\n usePersistedRefWithTTL,\r\n usePersistedState,\r\n useSharedState,\r\n useSimpleStore,\r\n useStore,\r\n useStoreDevtools,\r\n useStoreSnapshot,\r\n useSubscribe,\r\n useWatch,\r\n} from './composables'\r\n\r\nexport type {\r\n ActionContext,\r\n EventLogEntry,\r\n FormFieldRules,\r\n FormFieldState,\r\n FormValidator,\r\n OptimisticEntry,\r\n PersistedStateOptions,\r\n PersistedStateReturn,\r\n StateDiff,\r\n StateSnapshot,\r\n StoreInfo,\r\n UseFormStoreOptions,\r\n UseFormStoreReturn,\r\n UseOptimisticReturn,\r\n UseSharedStateOptions,\r\n UseSharedStateReturn,\r\n UseStoreDevtoolsReturn,\r\n UseStoreSnapshotOptions,\r\n UseStoreSnapshotReturn,\r\n} from './composables'\r\n\r\n// === 插件 ===\r\nexport {\r\n cleanupEnhancedPersist,\r\n createEnhancedPersistPlugin,\r\n createPersistPlugin,\r\n createStoreEnginePlugin,\r\n createStoreRegistryPlugin,\r\n createStoreVuePlugin,\r\n getGlobalStoreAPI,\r\n getStoreRegistry,\r\n} from './plugins'\r\n\r\nexport type {\r\n EnhancedPersistOptions,\r\n EnhancedPersistPluginOptions,\r\n PersistPluginOptions,\r\n StoreVuePluginOptions,\r\n} from './plugins'\r\n\r\n// === 类型 ===\r\nexport type {\r\n GlobalStoreAPI,\r\n MutationInfo,\r\n PersistOptions,\r\n StoreEnginePluginOptions,\r\n StoreOptions,\r\n StoreProviderProps,\r\n SubscribeOptions,\r\n UsePersistOptions,\r\n UsePersistReturn,\r\n UseStoreReturn,\r\n} from './types'\r\n\r\n// === 从 @composy/store-core 重新导出常用功能 ===\r\nexport {\r\n // 核心\r\n batch,\r\n // 工具\r\n combineSelectors,\r\n // 插件\r\n composePlugins,\r\n createAsyncAction,\r\n createBatchManager,\r\n createEventBus,\r\n // 中间件\r\n createLoggerMiddleware,\r\n createMiddlewareManager,\r\n createPluginFactory,\r\n createPubSub,\r\n createPubSubWithValue,\r\n createSelector,\r\n createTimeTravelMiddleware,\r\n createValidationMiddleware,\r\n deepClone,\r\n deepMerge,\r\n defineStorePlugin,\r\n globalEventBus,\r\n} from '@composy/store-core'\r\n\r\n// === 从 Pinia 重新导出 ===\r\nexport {\r\n createPinia,\r\n defineStore,\r\n getActivePinia,\r\n mapActions,\r\n mapGetters,\r\n mapState,\r\n mapStores,\r\n mapWritableState,\r\n setActivePinia,\r\n storeToRefs,\r\n} from 'pinia'\r\n\r\nexport type {\r\n Pinia,\r\n PiniaPlugin,\r\n PiniaPluginContext,\r\n StateTree,\r\n Store,\r\n StoreDefinition,\r\n StoreGeneric,\r\n StoreGetters,\r\n StoreState,\r\n} from 'pinia'\r\n\r\n// === 版本信息 ===\r\nexport const version = '0.0.1'\r\nexport const name = '@composy/store-vue'\r\n"],"mappings":"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"}