@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
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/esm/types.js CHANGED
@@ -2,7 +2,7 @@
2
2
  * ***********************************
3
3
  * @composy/store-vue v0.0.1 *
4
4
  * Built with rollup *
5
- * Build time: 2024-08-17 11:39:29 *
5
+ * Build time: 2024-10-12 17:40:04 *
6
6
  * Build mode: production *
7
7
  * Minified: No *
8
8
  * ***********************************
@@ -2,7 +2,7 @@
2
2
  * ***********************************
3
3
  * @composy/store-vue v0.0.1 *
4
4
  * Built with rollup *
5
- * Build time: 2024-08-17 11:39:29 *
5
+ * Build time: 2024-10-12 17:40:04 *
6
6
  * Build mode: production *
7
7
  * Minified: No *
8
8
  * ***********************************
@@ -1 +1 @@
1
- {"version":3,"file":"defineSetupStore.cjs","names":[],"sources":["../../src/composables/defineSetupStore.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"],"mappings":";;;;;;;;;;;;;;AAiEO,SAAS,iBACd,IACA,OACA;CACA,QAAA,GAAA,MAAA,YAAA,CAAmB,IAAI,KAAK;AAC9B;AA8BO,SAAS,iBACd,SAC+B;CAC/B,MAAM,SAAS,CAAC;CAChB,KAAA,MAAW,OAAO,OAAO,KAAK,OAAO,GACnC,OAAO,QAAA,GAAA,IAAA,IAAA,CAAW,QAAQ,IAAI;CAEhC,OAAO;AACT;AAyBO,SAAS,mBACd,SACmD;CACnD,MAAM,SAAS,CAAC;CAChB,KAAA,MAAW,OAAO,OAAO,KAAK,OAAO,GACnC,OAAO,QAAA,GAAA,IAAA,SAAA,CAAgB,QAAQ,IAAI;CAErC,OAAO;AACT"}
1
+ {"version":3,"file":"defineSetupStore.cjs","names":[],"sources":["../../src/composables/defineSetupStore.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"],"mappings":";;;;;;;;;;;;;;AAiEO,SAAS,iBAAoB,IAAY,OAAgB;CAC9D,QAAA,GAAA,MAAA,YAAA,CAAmB,IAAI,KAAK;AAC9B;AA8BO,SAAS,iBACd,SAC+B;CAC/B,MAAM,SAAS,CAAC;CAChB,KAAA,MAAW,OAAO,OAAO,KAAK,OAAO,GACnC,OAAO,QAAA,GAAA,IAAA,IAAA,CAAW,QAAQ,IAAI;CAEhC,OAAO;AACT;AAyBO,SAAS,mBACd,SACmD;CACnD,MAAM,SAAS,CAAC;CAChB,KAAA,MAAW,OAAO,OAAO,KAAK,OAAO,GACnC,OAAO,QAAA,GAAA,IAAA,SAAA,CAAgB,QAAQ,IAAI;CAIrC,OAAO;AACT"}
@@ -117,4 +117,3 @@ export declare function createStoreState<T extends Record<string, unknown>>(init
117
117
  export declare function createStoreGetters<T extends Record<string, () => unknown>>(getters: T): {
118
118
  [K in keyof T]: ComputedRef<ReturnType<T[K]>>;
119
119
  };
120
- //# sourceMappingURL=defineSetupStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defineSetupStore.d.cts","sourceRoot":"","sources":["../../src/composables/defineSetupStore.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,KAAK,CAAA;AAI3C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,+LAE7D;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChE,OAAO,EAAE,CAAC,GACT;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAAE,CAM/B;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,EACxE,OAAO,EAAE,CAAC,GACT;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,WAAW,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAAE,CAQnD"}
@@ -2,7 +2,7 @@
2
2
  * ***********************************
3
3
  * @composy/store-vue v0.0.1 *
4
4
  * Built with rollup *
5
- * Build time: 2024-08-17 11:39:29 *
5
+ * Build time: 2024-10-12 17:40:04 *
6
6
  * Build mode: production *
7
7
  * Minified: No *
8
8
  * ***********************************
@@ -7,18 +7,17 @@
7
7
  export { createStoreGetters, createStoreState, defineSetupStore, } from './defineSetupStore';
8
8
  export { email, maxLength, minLength, numberRange, pattern, required, useFormStore, } from './useFormStore';
9
9
  export type { FormFieldRules, FormFieldState, FormValidator, UseFormStoreOptions, UseFormStoreReturn, } from './useFormStore';
10
- export { useOptimistic, } from './useOptimistic';
11
- export type { OptimisticEntry, UseOptimisticReturn, } from './useOptimistic';
10
+ export { useOptimistic } from './useOptimistic';
11
+ export type { OptimisticEntry, UseOptimisticReturn } from './useOptimistic';
12
12
  export { usePersist, usePersistedRef, usePersistedRefWithTTL, } from './usePersist';
13
- export { usePersistedState, } from './usePersistedState';
13
+ export { usePersistedState } from './usePersistedState';
14
14
  export type { PersistedStateOptions, PersistedStateReturn, } from './usePersistedState';
15
- export { useSharedState, } from './useSharedState';
15
+ export { useSharedState } from './useSharedState';
16
16
  export type { UseSharedStateOptions, UseSharedStateReturn, } from './useSharedState';
17
- export { useComputedStore, useSimpleStore, useStore, } from './useStore';
18
- export { useStoreDevtools, } from './useStoreDevtools';
17
+ export { useComputedStore, useSimpleStore, useStore } from './useStore';
18
+ export { useStoreDevtools } from './useStoreDevtools';
19
19
  export type { EventLogEntry, StoreInfo, UseStoreDevtoolsReturn, } from './useStoreDevtools';
20
- export { useStoreSnapshot, } from './useStoreSnapshot';
20
+ export { useStoreSnapshot } from './useStoreSnapshot';
21
21
  export type { StateDiff, StateSnapshot, UseStoreSnapshotOptions, UseStoreSnapshotReturn, } from './useStoreSnapshot';
22
- export { useOnAction, useSubscribe, useWatch, } from './useSubscribe';
22
+ export { useOnAction, useSubscribe, useWatch } from './useSubscribe';
23
23
  export type { ActionContext } from './useSubscribe';
24
- //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.cts","sourceRoot":"","sources":["../../src/composables/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,oBAAoB,CAAA;AAE3B,OAAO,EACL,KAAK,EACL,SAAS,EACT,SAAS,EACT,WAAW,EACX,OAAO,EACP,QAAQ,EACR,YAAY,GACb,MAAM,gBAAgB,CAAA;AAEvB,YAAY,EACV,cAAc,EACd,cAAc,EACd,aAAa,EACb,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,gBAAgB,CAAA;AAEvB,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAE/C,YAAY,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAA;AAE3E,OAAO,EACL,UAAU,EACV,eAAe,EACf,sBAAsB,GACvB,MAAM,cAAc,CAAA;AAErB,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AAEvD,YAAY,EACV,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,qBAAqB,CAAA;AAE5B,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAEjD,YAAY,EACV,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,kBAAkB,CAAA;AAEzB,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAEvE,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAErD,YAAY,EACV,aAAa,EACb,SAAS,EACT,sBAAsB,GACvB,MAAM,oBAAoB,CAAA;AAE3B,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAErD,YAAY,EACV,SAAS,EACT,aAAa,EACb,uBAAuB,EACvB,sBAAsB,GACvB,MAAM,oBAAoB,CAAA;AAE3B,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAEpE,YAAY,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA"}
@@ -2,7 +2,7 @@
2
2
  * ***********************************
3
3
  * @composy/store-vue v0.0.1 *
4
4
  * Built with rollup *
5
- * Build time: 2024-08-17 11:39:29 *
5
+ * Build time: 2024-10-12 17:40:04 *
6
6
  * Build mode: production *
7
7
  * Minified: No *
8
8
  * ***********************************
@@ -1 +1 @@
1
- {"version":3,"file":"useFormStore.cjs","names":[],"sources":["../../src/composables/useFormStore.ts"],"sourcesContent":["/* ============================================================\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"],"mappings":"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"}
1
+ {"version":3,"file":"useFormStore.cjs","names":[],"sources":["../../src/composables/useFormStore.ts"],"sourcesContent":["/* ============================================================\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"],"mappings":"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"}
@@ -128,4 +128,3 @@ export declare function email(message?: string): FormValidator<string>;
128
128
  * @returns 数字校验器函数
129
129
  */
130
130
  export declare function numberRange(min: number, max: number, message?: string): FormValidator<number>;
131
- //# sourceMappingURL=useFormStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useFormStore.d.cts","sourceRoot":"","sources":["../../src/composables/useFormStore.ts"],"names":[],"mappings":"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"}
@@ -2,7 +2,7 @@
2
2
  * ***********************************
3
3
  * @composy/store-vue v0.0.1 *
4
4
  * Built with rollup *
5
- * Build time: 2024-08-17 11:39:29 *
5
+ * Build time: 2024-10-12 17:40:04 *
6
6
  * Build mode: production *
7
7
  * Minified: No *
8
8
  * ***********************************
@@ -17,39 +17,54 @@ function uid() {
17
17
  }
18
18
  function useOptimistic(initialState) {
19
19
  const state = (0, vue.ref)((0, _composy_store_core.deepClone)(initialState));
20
+ let committedState = (0, _composy_store_core.deepClone)(initialState);
21
+ const operations = [];
20
22
  const isPending = (0, vue.ref)(false);
21
23
  const isRolledBack = (0, vue.ref)(false);
22
24
  const pending = (0, vue.ref)([]);
25
+ const refresh = () => {
26
+ let nextState = (0, _composy_store_core.deepClone)(committedState);
27
+ operations.forEach((operation) => {
28
+ if (operation.status !== "rolled-back") nextState = operation.apply(nextState);
29
+ });
30
+ state.value = (0, _composy_store_core.deepClone)(nextState);
31
+ pending.value = operations.filter((operation) => operation.status === "pending").map((operation) => ({
32
+ id: operation.id,
33
+ snapshot: (0, _composy_store_core.deepClone)(operation.snapshot),
34
+ status: operation.status,
35
+ timestamp: operation.timestamp
36
+ }));
37
+ isPending.value = pending.value.length > 0;
38
+ };
39
+ const reconcile = () => {
40
+ while (operations[0] && operations[0].status !== "pending") {
41
+ const operation = operations.shift();
42
+ if (!operation || operation.status === "rolled-back") continue;
43
+ committedState = operation.apply((0, _composy_store_core.deepClone)(committedState));
44
+ }
45
+ refresh();
46
+ };
23
47
  async function mutate(optimisticValue, confirm) {
24
48
  const snapshot = (0, _composy_store_core.deepClone)(state.value);
25
- const entryId = uid();
26
- const entry = {
27
- id: entryId,
49
+ const apply = typeof optimisticValue === "function" ? (previous) => (0, _composy_store_core.deepClone)(optimisticValue((0, _composy_store_core.deepClone)(previous))) : () => (0, _composy_store_core.deepClone)(optimisticValue);
50
+ const operation = {
51
+ id: uid(),
28
52
  snapshot,
29
53
  status: "pending",
30
- timestamp: Date.now()
54
+ timestamp: Date.now(),
55
+ apply
31
56
  };
32
- pending.value = [...pending.value, entry];
33
- isPending.value = true;
57
+ operations.push(operation);
34
58
  isRolledBack.value = false;
35
- if (typeof optimisticValue === "function") state.value = optimisticValue(state.value);
36
- else state.value = optimisticValue;
59
+ refresh();
37
60
  try {
38
61
  await confirm();
39
- pending.value = pending.value.map((e) => e.id === entryId ? {
40
- ...e,
41
- status: "confirmed"
42
- } : e);
62
+ operation.status = "confirmed";
43
63
  } catch {
44
- state.value = snapshot;
64
+ operation.status = "rolled-back";
45
65
  isRolledBack.value = true;
46
- pending.value = pending.value.map((e) => e.id === entryId ? {
47
- ...e,
48
- status: "rolled-back"
49
- } : e);
50
66
  } finally {
51
- pending.value = pending.value.filter((e) => e.status === "pending");
52
- isPending.value = pending.value.length > 0;
67
+ reconcile();
53
68
  }
54
69
  }
55
70
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"useOptimistic.cjs","names":[],"sources":["../../src/composables/useOptimistic.ts"],"sourcesContent":["/**\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"],"mappings":";;;;;;;;;;;;;;AA+CA,SAAS,MAAc;CACrB,OAAO,GAAG,KAAK,IAAI,EAAC,GAAI,KAAK,OAAO,CAAA,CAAE,SAAS,EAAE,CAAA,CAAE,MAAM,GAAG,CAAC;AAC/D;AAgCO,SAAS,cAAiB,cAAyC;CACxE,MAAM,SAAA,GAAA,IAAA,IAAA,EAAA,GAAA,oBAAA,UAAA,CAAyB,YAAY,CAAC;CAC5C,MAAM,aAAA,GAAA,IAAA,IAAA,CAAgB,KAAK;CAC3B,MAAM,gBAAA,GAAA,IAAA,IAAA,CAAmB,KAAK;CAC9B,MAAM,WAAA,GAAA,IAAA,IAAA,CAAoC,CAAC,CAAC;CAE5C,eAAe,OACb,iBACA,SACe;EAEf,MAAM,YAAA,GAAA,oBAAA,UAAA,CAAwB,MAAM,KAAK;EACzC,MAAM,UAAU,IAAI;EACpB,MAAM,QAA4B;GAChC,IAAI;GACJ;GACA,QAAQ;GACR,WAAW,KAAK,IAAI;EACtB;EAEA,QAAQ,QAAQ,CAAC,GAAG,QAAQ,OAAO,KAAK;EACxC,UAAU,QAAQ;EAClB,aAAa,QAAQ;EAGrB,IAAI,OAAO,oBAAoB,YAC7B,MAAM,QAAS,gBAAmC,MAAM,KAAK;OAG7D,MAAM,QAAQ;EAGhB,IAAI;GAEF,MAAM,QAAQ;GAGd,QAAQ,QAAQ,QAAQ,MAAM,KAAI,MAChC,EAAE,OAAO,UAAU;IAAE,GAAG;IAAG,QAAQ;GAAqB,IAAI,CAC9D;EACF,QACM;GAEJ,MAAM,QAAQ;GACd,aAAa,QAAQ;GAErB,QAAQ,QAAQ,QAAQ,MAAM,KAAI,MAChC,EAAE,OAAO,UAAU;IAAE,GAAG;IAAG,QAAQ;GAAuB,IAAI,CAChE;EACF,UACA;GAEE,QAAQ,QAAQ,QAAQ,MAAM,QAAO,MAAK,EAAE,WAAW,SAAS;GAChE,UAAU,QAAQ,QAAQ,MAAM,SAAS;EAC3C;CACF;CAEA,OAAO;EACL;EACA;EACA;EACA;EACA;CACF;AACF"}
1
+ {"version":3,"file":"useOptimistic.cjs","names":[],"sources":["../../src/composables/useOptimistic.ts"],"sourcesContent":["/** 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"],"mappings":";;;;;;;;;;;;;;AA4BA,SAAS,MAAc;CACrB,OAAO,GAAG,KAAK,IAAI,EAAC,GAAI,KAAK,OAAO,CAAA,CAAE,SAAS,EAAE,CAAA,CAAE,MAAM,GAAG,CAAC;AAC/D;AAEO,SAAS,cAAiB,cAAyC;CACxE,MAAM,SAAA,GAAA,IAAA,IAAA,EAAA,GAAA,oBAAA,UAAA,CAAyB,YAAY,CAAC;CAC5C,IAAI,kBAAA,GAAA,oBAAA,UAAA,CAA2B,YAAY;CAC3C,MAAM,aAA6B,CAAC;CACpC,MAAM,aAAA,GAAA,IAAA,IAAA,CAAgB,KAAK;CAC3B,MAAM,gBAAA,GAAA,IAAA,IAAA,CAAmB,KAAK;CAC9B,MAAM,WAAA,GAAA,IAAA,IAAA,CAAoC,CAAC,CAAC;CAE5C,MAAM,gBAAsB;EAC1B,IAAI,aAAA,GAAA,oBAAA,UAAA,CAAsB,cAAc;EACxC,WAAW,SAAQ,cAAa;GAC9B,IAAI,UAAU,WAAW,eACvB,YAAY,UAAU,MAAM,SAAS;EAEzC,CAAC;EACD,MAAM,SAAA,GAAA,oBAAA,UAAA,CAAkB,SAAS;EACjC,QAAQ,QAAQ,WACb,QAAO,cAAa,UAAU,WAAW,SAAS,CAAA,CAClD,KAAI,eAAc;GACjB,IAAI,UAAU;GACd,WAAA,GAAA,oBAAA,UAAA,CAAoB,UAAU,QAAQ;GACtC,QAAQ,UAAU;GAClB,WAAW,UAAU;EACvB,EAAE;EACJ,UAAU,QAAQ,QAAQ,MAAM,SAAS;CAC3C;CAEA,MAAM,kBAAwB;EAC5B,OAAO,WAAW,MAAM,WAAW,EAAC,CAAE,WAAW,WAAW;GAC1D,MAAM,YAAY,WAAW,MAAM;GACnC,IAAI,CAAC,aAAa,UAAU,WAAW,eAAe;GACtD,iBAAiB,UAAU,OAAA,GAAA,oBAAA,UAAA,CAAgB,cAAc,CAAC;EAC5D;EACA,QAAQ;CACV;CAEA,eAAe,OACb,iBACA,SACe;EACf,MAAM,YAAA,GAAA,oBAAA,UAAA,CAAqB,MAAM,KAAK;EACtC,MAAM,QACJ,OAAO,oBAAoB,cACtB,cAAA,GAAA,oBAAA,UAAA,CACY,iBAAA,GAAA,oBAAA,UAAA,CAA6C,QAAQ,CAAC,CAAC,WAAA,GAAA,oBAAA,UAAA,CACjD,eAAe;EACxC,MAAM,YAA0B;GAC9B,IAAI,IAAI;GACR;GACA,QAAQ;GACR,WAAW,KAAK,IAAI;GACpB;EACF;EAEA,WAAW,KAAK,SAAS;EACzB,aAAa,QAAQ;EACrB,QAAQ;EAER,IAAI;GACF,MAAM,QAAQ;GACd,UAAU,SAAS;EACrB,QAAQ;GACN,UAAU,SAAS;GACnB,aAAa,QAAQ;EACvB,UAAE;GACA,UAAU;EACZ;CACF;CAEA,OAAO;EACL;EACA;EACA;EACA;EACA;CACF;AACF"}
@@ -0,0 +1,16 @@
1
+ /** Optimistic state helper with an operation log for overlapping mutations. */
2
+ import type { Ref } from 'vue';
3
+ export interface OptimisticEntry<T> {
4
+ id: string;
5
+ snapshot: T;
6
+ status: 'pending' | 'confirmed' | 'rolled-back';
7
+ timestamp: number;
8
+ }
9
+ export interface UseOptimisticReturn<T> {
10
+ state: Ref<T>;
11
+ isPending: Ref<boolean>;
12
+ pending: Ref<OptimisticEntry<T>[]>;
13
+ isRolledBack: Ref<boolean>;
14
+ mutate: (optimisticValue: T | ((prev: T) => T), confirm: () => Promise<void>) => Promise<void>;
15
+ }
16
+ export declare function useOptimistic<T>(initialState: T): UseOptimisticReturn<T>;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useOptimistic.d.cts","sourceRoot":"","sources":["../../src/composables/useOptimistic.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAE/E,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,KAAK,CAAA;AAI9B,MAAM,WAAW,eAAe,CAAC,CAAC;IAChC,EAAE,EAAE,MAAM,CAAA;IACV,QAAQ,EAAE,CAAC,CAAA;IACX,MAAM,EAAE,SAAS,GAAG,WAAW,GAAG,aAAa,CAAA;IAC/C,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,mBAAmB,CAAC,CAAC;IACpC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC,CAAA;IACb,SAAS,EAAE,GAAG,CAAC,OAAO,CAAC,CAAA;IACvB,OAAO,EAAE,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;IAClC,YAAY,EAAE,GAAG,CAAC,OAAO,CAAC,CAAA;IAC1B,MAAM,EAAE,CACN,eAAe,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EACrC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,KACzB,OAAO,CAAC,IAAI,CAAC,CAAA;CACnB;AAUD,wBAAgB,aAAa,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,GAAG,mBAAmB,CAAC,CAAC,CAAC,CA4ExE"}
@@ -2,7 +2,7 @@
2
2
  * ***********************************
3
3
  * @composy/store-vue v0.0.1 *
4
4
  * Built with rollup *
5
- * Build time: 2024-08-17 11:39:29 *
5
+ * Build time: 2024-10-12 17:40:04 *
6
6
  * Build mode: production *
7
7
  * Minified: No *
8
8
  * ***********************************
@@ -13,10 +13,14 @@ let vue = require("vue");
13
13
  //#region src/composables/usePersist.ts
14
14
  function getStorage(type) {
15
15
  if (typeof window === "undefined") return null;
16
- return type === "localStorage" ? localStorage : sessionStorage;
16
+ try {
17
+ return type === "localStorage" ? window.localStorage : window.sessionStorage;
18
+ } catch {
19
+ return null;
20
+ }
17
21
  }
18
22
  function usePersist(options) {
19
- const { key, storage: storageType = "localStorage", defaultValue, serialize = JSON.stringify, deserialize = JSON.parse } = options;
23
+ const { key, storage: storageType = "localStorage", defaultValue, serialize = JSON.stringify, deserialize = JSON.parse, restoreOnMounted = true } = options;
20
24
  const storage = getStorage(storageType);
21
25
  const value = (0, vue.ref)(defaultValue);
22
26
  const restore = () => {
@@ -45,7 +49,7 @@ function usePersist(options) {
45
49
  console.error(`[usePersist] \u6E05\u9664\u5931\u8D25 (${key}):`, error);
46
50
  }
47
51
  };
48
- (0, vue.onMounted)(() => {
52
+ if (restoreOnMounted) (0, vue.onMounted)(() => {
49
53
  restore();
50
54
  });
51
55
  (0, vue.watch)(value, () => {
@@ -67,12 +71,13 @@ function usePersistedRef(key, defaultValue, storage = "localStorage") {
67
71
  return value;
68
72
  }
69
73
  function usePersistedRefWithTTL(key, defaultValue, ttl) {
70
- const { value } = usePersist({
74
+ const persisted = usePersist({
71
75
  key,
72
76
  defaultValue: {
73
77
  value: defaultValue,
74
78
  expiry: 0
75
79
  },
80
+ restoreOnMounted: false,
76
81
  serialize: (data) => JSON.stringify(data),
77
82
  deserialize: (str) => {
78
83
  const data = JSON.parse(str);
@@ -83,13 +88,17 @@ function usePersistedRefWithTTL(key, defaultValue, ttl) {
83
88
  return data;
84
89
  }
85
90
  });
86
- const wrappedRef = (0, vue.ref)(value.value.value);
91
+ persisted.restore();
92
+ const wrappedRef = (0, vue.ref)(persisted.value.value.value);
87
93
  (0, vue.watch)(wrappedRef, (newValue) => {
88
- value.value = {
94
+ persisted.value.value = {
89
95
  value: newValue,
90
96
  expiry: Date.now() + ttl
91
97
  };
92
98
  });
99
+ (0, vue.watch)(persisted.value, (stored) => {
100
+ if (!Object.is(wrappedRef.value, stored.value)) wrappedRef.value = stored.value;
101
+ }, { deep: true });
93
102
  return wrappedRef;
94
103
  }
95
104
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"usePersist.cjs","names":[],"sources":["../../src/composables/usePersist.ts"],"sourcesContent":["/**\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"],"mappings":";;;;;;;;;;;;;AAcA,SAAS,WAAW,MAAyD;CAC3E,IAAI,OAAO,WAAW,aACpB,OAAO;CAET,OAAO,SAAS,iBAAiB,eAAe;AAClD;AAgCO,SAAS,WAAc,SAAiD;CAC7E,MAAM,EACJ,KACA,SAAS,cAAc,gBACvB,cACA,YAAY,KAAK,WACjB,cAAc,KAAK,UACjB;CAEJ,MAAM,UAAU,WAAW,WAAW;CACtC,MAAM,SAAA,GAAA,IAAA,IAAA,CAAe,YAAiB;CAKtC,MAAM,gBAAsB;EAC1B,IAAI,CAAC,SAAS;EAEd,IAAI;GACF,MAAM,SAAS,QAAQ,QAAQ,GAAG;GAClC,IAAI,WAAW,MACb,MAAM,QAAQ,YAAY,MAAM;EAEpC,SACO,OAAO;GACZ,QAAQ,MAAM,0CAAsB,IAAG,KAAM,KAAK;EACpD;CACF;CAKA,MAAM,aAAmB;EACvB,IAAI,CAAC,SAAS;EAEd,IAAI;GACF,QAAQ,QAAQ,KAAK,UAAU,MAAM,KAAK,CAAC;EAC7C,SACO,OAAO;GACZ,QAAQ,MAAM,0CAAsB,IAAG,KAAM,KAAK;EACpD;CACF;CAKA,MAAM,cAAoB;EACxB,IAAI,CAAC,SAAS;EAEd,IAAI;GACF,QAAQ,WAAW,GAAG;GACtB,MAAM,QAAQ;EAChB,SACO,OAAO;GACZ,QAAQ,MAAM,0CAAsB,IAAG,KAAM,KAAK;EACpD;CACF;CAGA,CAAA,GAAA,IAAA,UAAA,OAAgB;EACd,QAAQ;CACV,CAAC;CAGD,CAAA,GAAA,IAAA,MAAA,CACE,aACM;EACJ,KAAK;CACP,GACA,EAAE,MAAM,KAAK,CACf;CAEA,OAAO;EACL;EACA;EACA;EACA;CACF;AACF;AAmBO,SAAS,gBACd,KACA,cACA,UAA6C,gBACrC;CACR,MAAM,EAAE,UAAU,WAAc;EAC9B;EACA;EACA;CACF,CAAC;CACD,OAAO;AACT;AAWO,SAAS,uBACd,KACA,cACA,KACQ;CAMR,MAAM,EAAE,UAAU,WAAuB;EACvC;EACA,cAAc;GAAE,OAAO;GAAc,QAAQ;EAAE;EAC/C,YAAY,SAAkB,KAAK,UAAU,IAAI;EACjD,cAAc,QAAgB;GAC5B,MAAM,OAAO,KAAK,MAAM,GAAG;GAC3B,IAAI,KAAK,UAAU,KAAK,IAAI,IAAI,KAAK,QACnC,OAAO;IAAE,OAAO;IAAc,QAAQ;GAAE;GAE1C,OAAO;EACT;CACF,CAAC;CAGD,MAAM,cAAA,GAAA,IAAA,IAAA,CAAiB,MAAM,MAAM,KAAK;CAExC,CAAA,GAAA,IAAA,MAAA,CAAM,aAAa,aAAa;EAC9B,MAAM,QAAQ;GACZ,OAAO;GACP,QAAQ,KAAK,IAAI,IAAI;EACvB;CACF,CAAC;CAED,OAAO;AACT"}
1
+ {"version":3,"file":"usePersist.cjs","names":[],"sources":["../../src/composables/usePersist.ts"],"sourcesContent":["/**\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"],"mappings":";;;;;;;;;;;;;AAcA,SAAS,WAAW,MAAyD;CAC3E,IAAI,OAAO,WAAW,aACpB,OAAO;CAET,IAAI;EACF,OAAO,SAAS,iBAAiB,OAAO,eAAe,OAAO;CAChE,QAAQ;EACN,OAAO;CACT;AACF;AAgCO,SAAS,WAAc,SAAiD;CAC7E,MAAM,EACJ,KACA,SAAS,cAAc,gBACvB,cACA,YAAY,KAAK,WACjB,cAAc,KAAK,OACnB,mBAAmB,SACjB;CAEJ,MAAM,UAAU,WAAW,WAAW;CACtC,MAAM,SAAA,GAAA,IAAA,IAAA,CAAe,YAAiB;CAKtC,MAAM,gBAAsB;EAC1B,IAAI,CAAC,SAAS;EAEd,IAAI;GACF,MAAM,SAAS,QAAQ,QAAQ,GAAG;GAClC,IAAI,WAAW,MACb,MAAM,QAAQ,YAAY,MAAM;EAEpC,SAAS,OAAO;GACd,QAAQ,MAAM,0CAAsB,IAAG,KAAM,KAAK;EACpD;CACF;CAKA,MAAM,aAAmB;EACvB,IAAI,CAAC,SAAS;EAEd,IAAI;GACF,QAAQ,QAAQ,KAAK,UAAU,MAAM,KAAK,CAAC;EAC7C,SAAS,OAAO;GACd,QAAQ,MAAM,0CAAsB,IAAG,KAAM,KAAK;EACpD;CACF;CAKA,MAAM,cAAoB;EACxB,IAAI,CAAC,SAAS;EAEd,IAAI;GACF,QAAQ,WAAW,GAAG;GACtB,MAAM,QAAQ;EAChB,SAAS,OAAO;GACd,QAAQ,MAAM,0CAAsB,IAAG,KAAM,KAAK;EACpD;CACF;CAGA,IAAI,kBACF,CAAA,GAAA,IAAA,UAAA,OAAgB;EACd,QAAQ;CACV,CAAC;CAIH,CAAA,GAAA,IAAA,MAAA,CACE,aACM;EACJ,KAAK;CACP,GACA,EAAE,MAAM,KAAK,CACf;CAEA,OAAO;EACL;EACA;EACA;EACA;CACF;AACF;AAmBO,SAAS,gBACd,KACA,cACA,UAA6C,gBACrC;CACR,MAAM,EAAE,UAAU,WAAc;EAC9B;EACA;EACA;CACF,CAAC;CACD,OAAO;AACT;AAWO,SAAS,uBACd,KACA,cACA,KACQ;CAMR,MAAM,YAAY,WAAuB;EACvC;EACA,cAAc;GAAE,OAAO;GAAc,QAAQ;EAAE;EAC/C,kBAAkB;EAClB,YAAY,SAAkB,KAAK,UAAU,IAAI;EACjD,cAAc,QAAgB;GAC5B,MAAM,OAAO,KAAK,MAAM,GAAG;GAC3B,IAAI,KAAK,UAAU,KAAK,IAAI,IAAI,KAAK,QACnC,OAAO;IAAE,OAAO;IAAc,QAAQ;GAAE;GAE1C,OAAO;EACT;CACF,CAAC;CAGD,UAAU,QAAQ;CAClB,MAAM,cAAA,GAAA,IAAA,IAAA,CAAiB,UAAU,MAAM,MAAM,KAAK;CAElD,CAAA,GAAA,IAAA,MAAA,CAAM,aAAY,aAAY;EAC5B,UAAU,MAAM,QAAQ;GACtB,OAAO;GACP,QAAQ,KAAK,IAAI,IAAI;EACvB;CACF,CAAC;CAED,CAAA,GAAA,IAAA,MAAA,CACE,UAAU,QACV,WAAU;EACR,IAAI,CAAC,OAAO,GAAG,WAAW,OAAO,OAAO,KAAK,GAC3C,WAAW,QAAQ,OAAO;CAE9B,GACA,EAAE,MAAM,KAAK,CACf;CAEA,OAAO;AACT"}
@@ -65,4 +65,3 @@ export declare function usePersistedRef<T>(key: string, defaultValue: T, storage
65
65
  * @returns 持久化的 ref
66
66
  */
67
67
  export declare function usePersistedRefWithTTL<T>(key: string, defaultValue: T, ttl: number): Ref<T>;
68
- //# sourceMappingURL=usePersist.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usePersist.d.cts","sourceRoot":"","sources":["../../src/composables/usePersist.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,KAAK,CAAA;AAC9B,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAiBnE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,OAAO,EAAE,iBAAiB,GAAG,gBAAgB,CAAC,CAAC,CAAC,CA8E7E;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAC/B,GAAG,EAAE,MAAM,EACX,YAAY,EAAE,CAAC,EACf,OAAO,GAAE,cAAc,GAAG,gBAAiC,GAC1D,GAAG,CAAC,CAAC,CAAC,CAOR;AAED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,EACtC,GAAG,EAAE,MAAM,EACX,YAAY,EAAE,CAAC,EACf,GAAG,EAAE,MAAM,GACV,GAAG,CAAC,CAAC,CAAC,CA0CR"}
@@ -2,7 +2,7 @@
2
2
  * ***********************************
3
3
  * @composy/store-vue v0.0.1 *
4
4
  * Built with rollup *
5
- * Build time: 2024-08-17 11:39:29 *
5
+ * Build time: 2024-10-12 17:40:04 *
6
6
  * Build mode: production *
7
7
  * Minified: No *
8
8
  * ***********************************
@@ -31,6 +31,7 @@ function usePersistedState(options) {
31
31
  const storage = getStorage(storageType);
32
32
  const createDefaultValue = () => (0, _composy_store_core.deepClone)(defaultValue);
33
33
  const state = (0, vue.ref)(createDefaultValue());
34
+ let skipNextSave = false;
34
35
  const save = () => {
35
36
  if (!storage) return;
36
37
  try {
@@ -44,6 +45,7 @@ function usePersistedState(options) {
44
45
  console.error(`[usePersistedState] \u4FDD\u5B58\u5931\u8D25 (${key}):`, error);
45
46
  }
46
47
  };
48
+ const debouncedSave = debounceDelay > 0 ? (0, _composy_store_core.debounce)(save, debounceDelay) : null;
47
49
  const clear = () => {
48
50
  if (!storage) return;
49
51
  try {
@@ -71,6 +73,8 @@ function usePersistedState(options) {
71
73
  }
72
74
  };
73
75
  const reset = () => {
76
+ debouncedSave?.cancel();
77
+ skipNextSave = true;
74
78
  state.value = createDefaultValue();
75
79
  clear();
76
80
  };
@@ -85,9 +89,14 @@ function usePersistedState(options) {
85
89
  return false;
86
90
  }
87
91
  };
88
- const debouncedSave = debounceDelay > 0 ? (0, _composy_store_core.debounce)(save, debounceDelay) : null;
89
92
  const handleStorageEvent = (event) => {
90
- if (event.key !== key || !event.newValue) return;
93
+ if (event.key !== key) return;
94
+ if (!event.newValue) {
95
+ debouncedSave?.cancel();
96
+ skipNextSave = true;
97
+ state.value = createDefaultValue();
98
+ return;
99
+ }
91
100
  try {
92
101
  const item = JSON.parse(event.newValue);
93
102
  if (item.expiresAt && Date.now() > item.expiresAt) return;
@@ -98,6 +107,10 @@ function usePersistedState(options) {
98
107
  };
99
108
  restore();
100
109
  (0, vue.watch)(state, (newValue, oldValue) => {
110
+ if (skipNextSave) {
111
+ skipNextSave = false;
112
+ return;
113
+ }
101
114
  if (onBeforeChange?.(newValue, oldValue) === false) return;
102
115
  if (debouncedSave) debouncedSave();
103
116
  else save();