@cmtlyt/lingshu-toolkit 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (211) hide show
  1. package/README.md +10 -0
  2. package/dist/665.js +1 -0
  3. package/dist/893.js +1 -0
  4. package/dist/react/index.js +1 -205
  5. package/dist/react/use-boolean/index.d.ts +2 -1
  6. package/dist/react/use-boolean/index.js +1 -16
  7. package/dist/react/use-controllable-value/index.d.ts +3 -3
  8. package/dist/react/use-controllable-value/index.js +1 -32
  9. package/dist/react/use-counter/index.d.ts +2 -2
  10. package/dist/react/use-counter/index.js +1 -49
  11. package/dist/react/use-force-update/index.d.ts +2 -1
  12. package/dist/react/use-force-update/index.js +1 -6
  13. package/dist/react/use-mount/index.d.ts +2 -1
  14. package/dist/react/use-mount/index.js +1 -16
  15. package/dist/react/use-ref-state/index.d.ts +3 -2
  16. package/dist/react/use-ref-state/index.js +1 -33
  17. package/dist/react/use-storage/index.d.ts +2 -1
  18. package/dist/react/use-storage/index.js +1 -15
  19. package/dist/react/use-title/index.d.ts +2 -2
  20. package/dist/react/use-title/index.js +1 -24
  21. package/dist/react/use-toggle/index.d.ts +4 -4
  22. package/dist/react/use-toggle/index.js +1 -26
  23. package/dist/react/use-valid-data/index.d.ts +5 -4
  24. package/dist/react/use-valid-data/index.js +1 -14
  25. package/dist/shared/allx/index.d.ts +2 -1
  26. package/dist/shared/allx/index.js +1 -44
  27. package/dist/shared/allx/types.d.ts +6 -0
  28. package/dist/shared/allx/utils.d.ts +9 -7
  29. package/dist/shared/allx/utils.js +1 -94
  30. package/dist/shared/animation/index.d.ts +3 -2
  31. package/dist/shared/animation/index.js +1 -77
  32. package/dist/shared/animation/types.d.ts +8 -0
  33. package/dist/shared/animation/utils.d.ts +3 -10
  34. package/dist/shared/animation/utils.js +1 -134
  35. package/dist/shared/api-controller/create-api.js +1 -79
  36. package/dist/shared/api-controller/index.js +1 -3
  37. package/dist/shared/api-controller/request.js +1 -66
  38. package/dist/shared/api-controller/types.d.ts +26 -27
  39. package/dist/shared/api-controller/utils.d.ts +6 -15
  40. package/dist/shared/api-controller/utils.js +1 -96
  41. package/dist/shared/condition-merge/index.d.ts +6 -6
  42. package/dist/shared/condition-merge/index.js +1 -30
  43. package/dist/shared/create-storage-handler/index.d.ts +4 -3
  44. package/dist/shared/create-storage-handler/index.js +1 -68
  45. package/dist/shared/data-handler/index.d.ts +4 -3
  46. package/dist/shared/data-handler/index.js +1 -77
  47. package/dist/shared/data-handler/tools.d.ts +6 -23
  48. package/dist/shared/data-handler/tools.js +1 -48
  49. package/dist/shared/data-handler/types.d.ts +20 -2
  50. package/dist/shared/data-mixed-manager/constants.js +1 -9
  51. package/dist/shared/data-mixed-manager/index.js +1 -226
  52. package/dist/shared/data-mixed-manager/types.d.ts +1 -2
  53. package/dist/shared/index.d.ts +2 -0
  54. package/dist/shared/index.js +1 -957
  55. package/dist/shared/lock-data/__test__/_helpers/memory-adapters.d.ts +95 -0
  56. package/dist/shared/lock-data/__test__/_helpers/memory-adapters.js +1 -0
  57. package/dist/shared/lock-data/__test__/playground.js +1 -0
  58. package/dist/shared/lock-data/adapters/authority.d.ts +40 -0
  59. package/dist/shared/lock-data/adapters/authority.js +1 -0
  60. package/dist/shared/lock-data/adapters/channel.d.ts +39 -0
  61. package/dist/shared/lock-data/adapters/channel.js +1 -0
  62. package/dist/shared/lock-data/adapters/index.d.ts +58 -0
  63. package/dist/shared/lock-data/adapters/index.js +1 -0
  64. package/dist/shared/lock-data/adapters/logger.d.ts +56 -0
  65. package/dist/shared/lock-data/adapters/logger.js +1 -0
  66. package/dist/shared/lock-data/adapters/session-store.d.ts +37 -0
  67. package/dist/shared/lock-data/adapters/session-store.js +1 -0
  68. package/dist/shared/lock-data/authority/epoch.d.ts +135 -0
  69. package/dist/shared/lock-data/authority/epoch.js +1 -0
  70. package/dist/shared/lock-data/authority/extract.d.ts +107 -0
  71. package/dist/shared/lock-data/authority/extract.js +1 -0
  72. package/dist/shared/lock-data/authority/index.d.ts +182 -0
  73. package/dist/shared/lock-data/authority/index.js +1 -0
  74. package/dist/shared/lock-data/authority/serialize.d.ts +35 -0
  75. package/dist/shared/lock-data/authority/serialize.js +1 -0
  76. package/dist/shared/lock-data/constants.d.ts +46 -0
  77. package/dist/shared/lock-data/constants.js +1 -0
  78. package/dist/shared/lock-data/core/actions-helpers.d.ts +163 -0
  79. package/dist/shared/lock-data/core/actions-helpers.js +1 -0
  80. package/dist/shared/lock-data/core/actions.d.ts +72 -0
  81. package/dist/shared/lock-data/core/actions.js +1 -0
  82. package/dist/shared/lock-data/core/draft.d.ts +64 -0
  83. package/dist/shared/lock-data/core/draft.js +1 -0
  84. package/dist/shared/lock-data/core/entry.d.ts +133 -0
  85. package/dist/shared/lock-data/core/entry.js +1 -0
  86. package/dist/shared/lock-data/core/fanout.d.ts +42 -0
  87. package/dist/shared/lock-data/core/fanout.js +1 -0
  88. package/dist/shared/lock-data/core/readonly-view.d.ts +49 -0
  89. package/dist/shared/lock-data/core/readonly-view.js +1 -0
  90. package/dist/shared/lock-data/core/registry.d.ts +282 -0
  91. package/dist/shared/lock-data/core/registry.js +1 -0
  92. package/dist/shared/lock-data/core/signal.d.ts +33 -0
  93. package/dist/shared/lock-data/core/signal.js +1 -0
  94. package/dist/shared/lock-data/drivers/broadcast-protocol.d.ts +71 -0
  95. package/dist/shared/lock-data/drivers/broadcast-protocol.js +1 -0
  96. package/dist/shared/lock-data/drivers/broadcast-state.d.ts +125 -0
  97. package/dist/shared/lock-data/drivers/broadcast-state.js +1 -0
  98. package/dist/shared/lock-data/drivers/broadcast.d.ts +36 -0
  99. package/dist/shared/lock-data/drivers/broadcast.js +1 -0
  100. package/dist/shared/lock-data/drivers/custom.d.ts +27 -0
  101. package/dist/shared/lock-data/drivers/custom.js +1 -0
  102. package/dist/shared/lock-data/drivers/index.d.ts +59 -0
  103. package/dist/shared/lock-data/drivers/index.js +1 -0
  104. package/dist/shared/lock-data/drivers/local.d.ts +86 -0
  105. package/dist/shared/lock-data/drivers/local.js +1 -0
  106. package/dist/shared/lock-data/drivers/storage-protocol.d.ts +67 -0
  107. package/dist/shared/lock-data/drivers/storage-protocol.js +1 -0
  108. package/dist/shared/lock-data/drivers/storage-state.d.ts +103 -0
  109. package/dist/shared/lock-data/drivers/storage-state.js +1 -0
  110. package/dist/shared/lock-data/drivers/storage.d.ts +71 -0
  111. package/dist/shared/lock-data/drivers/storage.js +1 -0
  112. package/dist/shared/lock-data/drivers/types.d.ts +73 -0
  113. package/dist/shared/lock-data/drivers/types.js +0 -0
  114. package/dist/shared/lock-data/drivers/web-locks.d.ts +123 -0
  115. package/dist/shared/lock-data/drivers/web-locks.js +1 -0
  116. package/dist/shared/lock-data/errors/index.d.ts +12 -0
  117. package/dist/shared/lock-data/errors/index.js +1 -0
  118. package/dist/shared/lock-data/errors/invalid-options-error.d.ts +11 -0
  119. package/dist/shared/lock-data/errors/invalid-options-error.js +1 -0
  120. package/dist/shared/lock-data/errors/lock-aborted-error.d.ts +10 -0
  121. package/dist/shared/lock-data/errors/lock-aborted-error.js +1 -0
  122. package/dist/shared/lock-data/errors/lock-disposed-error.d.ts +14 -0
  123. package/dist/shared/lock-data/errors/lock-disposed-error.js +1 -0
  124. package/dist/shared/lock-data/errors/lock-revoked-error.d.ts +10 -0
  125. package/dist/shared/lock-data/errors/lock-revoked-error.js +1 -0
  126. package/dist/shared/lock-data/errors/lock-timeout-error.d.ts +9 -0
  127. package/dist/shared/lock-data/errors/lock-timeout-error.js +1 -0
  128. package/dist/shared/lock-data/errors/readonly-mutation-error.d.ts +11 -0
  129. package/dist/shared/lock-data/errors/readonly-mutation-error.js +1 -0
  130. package/dist/shared/lock-data/index.d.ts +57 -0
  131. package/dist/shared/lock-data/index.js +1 -0
  132. package/dist/shared/lock-data/types.d.ts +347 -0
  133. package/dist/shared/lock-data/types.js +0 -0
  134. package/dist/shared/lock-data/utils/json-safe.d.ts +69 -0
  135. package/dist/shared/lock-data/utils/json-safe.js +1 -0
  136. package/dist/shared/logger/index.d.ts +2 -2
  137. package/dist/shared/logger/index.js +1 -10
  138. package/dist/shared/priority-queue/index.d.ts +45 -0
  139. package/dist/shared/priority-queue/index.js +1 -0
  140. package/dist/shared/priority-queue/types.d.ts +10 -0
  141. package/dist/shared/priority-queue/types.js +0 -0
  142. package/dist/shared/priority-queue/utils.d.ts +7 -0
  143. package/dist/shared/priority-queue/utils.js +1 -0
  144. package/dist/shared/throw-error/index.d.ts +11 -3
  145. package/dist/shared/throw-error/index.js +1 -10
  146. package/dist/shared/try-call/index.d.ts +3 -3
  147. package/dist/shared/try-call/index.js +1 -59
  148. package/dist/shared/types/index.js +1 -2
  149. package/dist/shared/types/pack.d.ts +2 -2
  150. package/dist/shared/types/pack.js +1 -1
  151. package/dist/shared/utils/base.d.ts +1 -1
  152. package/dist/shared/utils/base.js +1 -6
  153. package/dist/shared/utils/index.js +1 -2
  154. package/dist/shared/utils/verify.d.ts +1 -1
  155. package/dist/shared/utils/verify.js +1 -67
  156. package/dist/shared/with-resolvers/index.d.ts +5 -3
  157. package/dist/shared/with-resolvers/index.js +1 -15
  158. package/dist/vue/index.js +1 -29
  159. package/dist/vue/use-title/index.d.ts +2 -2
  160. package/dist/vue/use-title/index.js +1 -29
  161. package/package.json +27 -27
  162. package/dist/247.js +0 -66
  163. package/dist/707.js +0 -142
  164. package/dist/react/use-force-update/index.test.d.ts +0 -1
  165. package/dist/react/use-mount/index.test.d.ts +0 -1
  166. package/dist/react/use-ref-state/index.test.d.ts +0 -1
  167. package/dist/react/use-storage/index.test.d.ts +0 -1
  168. package/dist/react/use-title/index.test.d.ts +0 -1
  169. package/dist/react/use-toggle/index.test.d.ts +0 -1
  170. package/dist/react/use-valid-data/index.test.d.ts +0 -1
  171. package/dist/shared/allx/__test__/allsettled.test.d.ts +0 -1
  172. package/dist/shared/allx/__test__/basic.test.d.ts +0 -1
  173. package/dist/shared/allx/__test__/circular-dependency.test.d.ts +0 -1
  174. package/dist/shared/allx/__test__/dependency.test.d.ts +0 -1
  175. package/dist/shared/allx/__test__/edge-cases.test.d.ts +0 -1
  176. package/dist/shared/allx/__test__/error-handling.test.d.ts +0 -1
  177. package/dist/shared/allx/__test__/execution-order.test.d.ts +0 -1
  178. package/dist/shared/allx/__test__/falsy-values.test.d.ts +0 -1
  179. package/dist/shared/allx/__test__/performance.test.d.ts +0 -1
  180. package/dist/shared/allx/__test__/type-checking.test.d.ts +0 -1
  181. package/dist/shared/allx/__test__/use-cases.test.d.ts +0 -1
  182. package/dist/shared/animation/__test__/animation-pause-resume.test.d.ts +0 -1
  183. package/dist/shared/animation/__test__/animation.test.d.ts +0 -1
  184. package/dist/shared/animation/__test__/step-animation.test.d.ts +0 -1
  185. package/dist/shared/animation/__test__/utils.test.d.ts +0 -1
  186. package/dist/shared/api-controller/__test__/index.browser.test.d.ts +0 -1
  187. package/dist/shared/api-controller/__test__/index.node.test.d.ts +0 -1
  188. package/dist/shared/condition-merge/index.test-d.js +0 -108
  189. package/dist/shared/condition-merge/index.test.d.ts +0 -1
  190. package/dist/shared/create-storage-handler/index.browser.test.d.ts +0 -1
  191. package/dist/shared/create-storage-handler/index.test.d.ts +0 -1
  192. package/dist/shared/data-handler/index.test.d.ts +0 -1
  193. package/dist/shared/data-mixed-manager/__test__/basic.test.d.ts +0 -1
  194. package/dist/shared/data-mixed-manager/__test__/build-options.test.d.ts +0 -1
  195. package/dist/shared/data-mixed-manager/__test__/constructor-options.test.d.ts +0 -1
  196. package/dist/shared/data-mixed-manager/__test__/data-management.test.d.ts +0 -1
  197. package/dist/shared/data-mixed-manager/__test__/edge-cases.test.d.ts +0 -1
  198. package/dist/shared/data-mixed-manager/__test__/events.browser.test.d.ts +0 -1
  199. package/dist/shared/data-mixed-manager/__test__/events.test.d.ts +0 -1
  200. package/dist/shared/data-mixed-manager/__test__/fixed-slots.test.d.ts +0 -1
  201. package/dist/shared/data-mixed-manager/__test__/insert-mode.test.d.ts +0 -1
  202. package/dist/shared/throw-error/index.test.d.ts +0 -1
  203. package/dist/shared/try-call/index.test.d.ts +0 -1
  204. package/dist/shared/utils/__test__/base.test.d.ts +0 -1
  205. package/dist/shared/utils/__test__/verify.test.d.ts +0 -1
  206. package/dist/shared/with-resolvers/index.test.d.ts +0 -1
  207. package/dist/test/utils.d.ts +0 -13
  208. package/dist/vue/use-title/index.test.d.ts +0 -1
  209. /package/dist/{react/use-boolean/index.test.d.ts → shared/lock-data/__test__/index.test-d.d.ts} +0 -0
  210. /package/dist/{react/use-controllable-value/index.test.d.ts → shared/lock-data/__test__/integration/entry.test-d.d.ts} +0 -0
  211. /package/dist/{react/use-counter/index.test.d.ts → shared/lock-data/__test__/playground.d.ts} +0 -0
@@ -0,0 +1,71 @@
1
+ /**
2
+ * StorageDriver:基于 localStorage 的跨 Tab 互斥锁
3
+ *
4
+ * 适用场景(由 pickDriver 决定):
5
+ * - 浏览器环境但既不支持 `navigator.locks`,也不支持 `BroadcastChannel`
6
+ * - 显式 `mode='storage'` 强制指定
7
+ *
8
+ * 本文件是工厂聚合层:
9
+ * - 前置依赖校验(id 必传 / localStorage 可用)
10
+ * - 构造 state 容器 + 订阅 storage 事件 + 启动 polling
11
+ * - 暴露 acquire / destroy
12
+ *
13
+ * 协议细节、状态机、CAS 读写、队列操作、drain 逻辑分别放在:
14
+ * - `./storage-protocol`:存储格式 + 常量 + 校验 + nonce 生成
15
+ * - `./storage-state`:状态机 + CAS 读写 + 队列 + 心跳 + drain
16
+ */
17
+ import type { LockDriverContext, LockDriverHandle } from '../types';
18
+ import { type StorageDriverState, type Waiter } from './storage-state';
19
+ import type { LockDriver, LockDriverDeps } from './types';
20
+ /**
21
+ * 能力探测:localStorage 是否可实际读写
22
+ *
23
+ * 与 adapters/authority 的探测同构;pickDriver 已做一次但保留防御性兜底,
24
+ * 覆盖单元测试直接实例化的场景
25
+ */
26
+ declare function hasUsableLocalStorage(): boolean;
27
+ /**
28
+ * 构造 waiter 并绑定 signal / timeout 的 abort 生命周期
29
+ *
30
+ * 与 broadcast driver 的 buildWaiter 同构;主要差异:
31
+ * - 没有 pendingAnnounce / pendingForce 分支(storage driver 的抢锁是 CAS 直接决断,
32
+ * 没有"进行中的竞选"需要清理)
33
+ * - abort 时需要 removeWaiter + pumpNextWaiter,让下一个 waiter 继续抢
34
+ */
35
+ declare function buildWaiter(ctx: LockDriverContext, state: StorageDriverState, resolve: (handle: LockDriverHandle) => void, reject: (error: Error) => void): Waiter;
36
+ /**
37
+ * force 路径的专用入队 + 抢锁流程
38
+ *
39
+ * force 不走 FIFO 队列 —— 直接 CAS 覆盖 holder;成功即进入 holding,失败交由 abort 处理
40
+ */
41
+ declare function acquireForceLock(state: StorageDriverState, waiter: Waiter): void;
42
+ /**
43
+ * 快路径抢锁成功时的后处理
44
+ *
45
+ * 把 `acquireNonForceLock` 的快路径 `.then` 回调拆出为独立函数,
46
+ * 降低外层函数圈复杂度;职责不变 —— 根据 destroyed / settled / status 决定:
47
+ * - 直接授予 handle
48
+ * - 释放刚抢到的锁并 abort waiter
49
+ * - 释放刚抢到的锁并降级到慢路径入队
50
+ */
51
+ declare function handleFastPathGrant(state: StorageDriverState, waiter: Waiter, grantNonce: string): void;
52
+ /**
53
+ * 非 force 路径的抢锁流程
54
+ *
55
+ * 1. 快路径:本地 idle + 无其他 waiter + storage 可直接抢 → tryAcquire 一次,成功即返回
56
+ * 2. 慢路径:入 storage 队列 + 本地队列 → 等 pumpNextWaiter(由 storage 事件 / polling / release 触发)
57
+ */
58
+ declare function acquireNonForceLock(state: StorageDriverState, waiter: Waiter): void;
59
+ /**
60
+ * 把 waiter 加入 storage 队列 + 本地队列
61
+ *
62
+ * storage 入队失败(quota / 写冲突重试耗尽)不阻塞 —— 让 waiter 本地排队等 polling / heartbeat
63
+ * 超时触发 pump;最坏情况下会触发 acquireTimeout
64
+ */
65
+ declare function enqueueSlowPath(state: StorageDriverState, waiter: Waiter): void;
66
+ declare function acquireStorageLock(state: StorageDriverState, ctx: LockDriverContext): Promise<LockDriverHandle>;
67
+ /**
68
+ * 创建 StorageDriver 实例
69
+ */
70
+ declare function createStorageDriver(deps: LockDriverDeps): LockDriver;
71
+ export { acquireForceLock, acquireNonForceLock, acquireStorageLock, buildWaiter, createStorageDriver, enqueueSlowPath, handleFastPathGrant, hasUsableLocalStorage, };
@@ -0,0 +1 @@
1
+ import{throwError as e}from"../../throw-error/index.js";import{isNumber as t,isString as r}from"../../utils/index.js";import{ERROR_FN_NAME as o,LOCK_PREFIX as n}from"../constants.js";import{LockAbortedError as i,LockTimeoutError as s}from"../errors/index.js";import{canFastAcquire as a,drainOnDestroy as l,enqueueInStorage as u,enterHolding as d,pumpNextWaiter as c,releaseHolderInStorage as k,removeWaiter as g,revokeHolding as f,startPolling as m,subscribeStorageEvent as $,tryAcquire as v}from"./storage-state.js";function b(){try{let e=globalThis.localStorage;if(!e)return!1;let t=`${n}:__storage_driver_probe__`;return e.setItem(t,"1"),e.removeItem(t),!0}catch{return!1}}function h(e,r,n,a){let l=!1,u=null;function d(){null!==u&&(clearTimeout(u),u=null),e.signal.removeEventListener("abort",f)}let k={token:e.token,resolve:e=>{l||(l=!0,d(),n(e))},reject:e=>{l||(l=!0,d(),a(e))},abort:e=>{l||(g(r.waiters,k),k.reject(e),c(r))},isSettled:()=>l};function f(){k.abort(new i(`[@cmtlyt/lingshu-toolkit#${o}]: acquire aborted (token=${e.token})`))}if(e.signal.aborted)return queueMicrotask(()=>f()),k;if(e.signal.addEventListener("abort",f,{once:!0}),t(e.acquireTimeout)&&e.acquireTimeout>0){let t=e.acquireTimeout;u=setTimeout(()=>{k.abort(new s(`[@cmtlyt/lingshu-toolkit#${o}]: acquire timed out after ${t}ms (token=${e.token})`))},t)}return k}function y(e,t){let{name:r,logger:n}=e.deps;v(e,t.token,!0).then(s=>{if(null===s)return void t.abort(new i(`[@cmtlyt/lingshu-toolkit#${o}]: force acquire failed after retries (token=${t.token})`));if(e.destroyed){k(e,t.token,s.nonce),t.abort(new i(`[@cmtlyt/lingshu-toolkit#${o}]: storage driver destroyed during force acquire (token=${t.token})`));return}if(t.isSettled())return void k(e,t.token,s.nonce);"holding"!==e.status.kind||e.status.released||f(e,"force");let a=d(e,t.token,s.nonce);n.debug(`[${r}] storage driver: grant (force) token=${t.token}`),t.resolve(a)})}function p(e,t,r){let{name:n,logger:s}=e.deps;if(e.destroyed){k(e,t.token,r),t.abort(new i(`[@cmtlyt/lingshu-toolkit#${o}]: storage driver destroyed during fast acquire (token=${t.token})`));return}if(t.isSettled())return void k(e,t.token,r);if("idle"!==e.status.kind){k(e,t.token,r),w(e,t);return}let a=d(e,t.token,r);s.debug(`[${n}] storage driver: grant (fast-path) token=${t.token}`),t.resolve(a)}function q(e,t){a(e)?v(e,t.token,!1).then(r=>{null!==r?p(e,t,r.nonce):t.isSettled()||w(e,t)}):w(e,t)}function w(e,t){let{name:r,logger:o}=e.deps;e.waiters.push(t),o.debug(`[${r}] storage driver: enqueue token=${t.token}, queue=${e.waiters.length}`),u(e,t.token).then(n=>{n||o.warn(`[${r}] storage driver: enqueueInStorage failed after retries (token=${t.token}); relying on timeout/polling`),c(e)})}function S(e,t){return e.destroyed?Promise.reject(new i(`[@cmtlyt/lingshu-toolkit#${o}]: storage driver has been destroyed (token=${t.token})`)):new Promise((r,o)=>{let n=h(t,e,r,o);t.force?y(e,n):q(e,n)})}function T(t){let{id:s}=t;r(s)&&0!==s.length||e(o,"storage driver requires a non-empty id",TypeError),b()||e(o,"storage driver requires a usable localStorage",TypeError);let a={deps:t,storage:globalThis.localStorage,key:`${n}:${s}:driver-lock`,status:{kind:"idle"},waiters:[],destroyed:!1,pumping:!1,unsubscribeStorageEvent:null,pollTimer:null};function u(e){return new i(`[@cmtlyt/lingshu-toolkit#${o}]: storage driver destroyed (token=${e})`)}return a.unsubscribeStorageEvent=$(a),a.pollTimer=m(a),{acquire:e=>S(a,e),destroy:()=>{a.destroyed||(a.destroyed=!0,l(a,u))}}}export{y as acquireForceLock,q as acquireNonForceLock,S as acquireStorageLock,h as buildWaiter,T as createStorageDriver,w as enqueueSlowPath,p as handleFastPathGrant,b as hasUsableLocalStorage};
@@ -0,0 +1,73 @@
1
+ /**
2
+ * drivers 层的内部类型契约
3
+ *
4
+ * 对应 RFC.md「架构分层」与「能力检测与降级」章节:
5
+ * - `LockDriver`:drivers 层统一抽象;Entry 持有 driver,所有 action 走 `driver.acquire`
6
+ * 拿到一个 `LockHandle`;driver 是**按 id 进程内单例**的(由 InstanceRegistry 持有)
7
+ * - `LockHandle`(已在 `../types` 中以 `LockDriverHandle` 名义向用户暴露):单次持有
8
+ * 的生命周期对象,`release` 还锁、`onRevokedByDriver` 桥接驱逐事件
9
+ * - `LockDriverDeps`:driver 构造依赖;不同 driver 子集不同,工厂函数签名统一
10
+ *
11
+ * 本文件仅导出**内部类型**,不对外 re-export。
12
+ */
13
+ import type { ResolvedLoggerAdapter } from '../adapters/logger';
14
+ import type { ChannelAdapter, ChannelAdapterContext, LockDataAdapters, LockDriverContext, LockDriverHandle } from '../types';
15
+ /**
16
+ * driver 构造依赖;由 `pickDriver` 从 `entry.adapters` 中分拣后传入
17
+ *
18
+ * 字段设计原则:
19
+ * - `logger` 对所有 driver 必传;driver 内部统一用此 logger 输出 warn / error / debug
20
+ * - `name` 已是拼好 `${LOCK_PREFIX}:${id}` 的完整锁作用域名;driver 无需关心前缀规则
21
+ * - `getChannel` 仅 `BroadcastDriver` 需要;其他 driver 允许为 undefined
22
+ * - `userGetLock` 仅 `CustomDriver` 需要;其他 driver 为 undefined
23
+ *
24
+ * **关于 `userGetLock` 的 `unknown` 泛型**:
25
+ * `LockDataAdapters<T>` 的泛型参数 `_T` 在 `getLock` 签名中并不出现(getLock
26
+ * 只接触锁调度上下文,不接触数据类型);此处用 `unknown` 表示"本 driver 层对
27
+ * 数据类型不可见",等价于 `LockDataAdapters<any>['getLock']` 但无 any 污染
28
+ */
29
+ interface LockDriverDeps {
30
+ /** 已拼前缀的锁作用域名 `${LOCK_PREFIX}:${id}`;无 id 场景下为 `${LOCK_PREFIX}:__local__` 占位 */
31
+ readonly name: string;
32
+ /** lockData 的原始 id;CustomDriver / 日志输出时需要(未 scope 化) */
33
+ readonly id: string | undefined;
34
+ /** Resolved logger(三方法齐全,下游可直接调用无需 guard) */
35
+ readonly logger: ResolvedLoggerAdapter;
36
+ /** 工厂:提供广播通道(仅 BroadcastDriver 消费) */
37
+ readonly getChannel?: (ctx: ChannelAdapterContext) => ChannelAdapter | null;
38
+ /** 用户注入的自定义 `adapters.getLock`(仅 CustomDriver 消费) */
39
+ readonly userGetLock?: LockDataAdapters<unknown>['getLock'];
40
+ }
41
+ /**
42
+ * 锁驱动统一抽象
43
+ *
44
+ * 所有 driver 实现均为"进程内 + id 作用域"单例;由 InstanceRegistry 首次创建
45
+ * Entry 时构造一次,Entry 销毁时调用 `destroy()` 清理内部长生命周期资源
46
+ * (心跳定时器、订阅、BroadcastChannel 实例等)
47
+ *
48
+ * 并发语义:
49
+ * - `acquire` 可被同一 driver 实例并发调用(例如同进程内两个 lockData 实例,均指向
50
+ * 同一 id),driver 内部负责**串行化**(FIFO 排队 / WebLocks 原生排队 / token 协议)
51
+ * - `force: true` 的 `acquire` 会让当前持有者的 `LockHandle.onRevokedByDriver` 以
52
+ * `'force'` 回调并立即释放;当前持有者后续 `release` 仍需可调用(幂等 no-op)
53
+ * - `acquireTimeout` 在 `acquire` 内部处理;到期抛 `LockTimeoutError`
54
+ * - `signal.aborted`(acquiring 阶段)抛 `LockAbortedError`
55
+ */
56
+ interface LockDriver {
57
+ /**
58
+ * 抢锁;返回一个单次持有的 `LockHandle`
59
+ *
60
+ * 抛错语义:
61
+ * - `LockTimeoutError`:acquireTimeout 到期
62
+ * - `LockAbortedError`:ctx.signal aborted / driver 已 destroy
63
+ * - 其他错误:driver 内部故障(logger.error 后原样透传)
64
+ */
65
+ readonly acquire: (ctx: LockDriverContext) => Promise<LockDriverHandle>;
66
+ /**
67
+ * Entry 引用计数归零 / dispose 时调用;清理 driver 内部长生命周期资源
68
+ *
69
+ * 幂等:重复调用 no-op;`destroy` 后再次 `acquire` 必抛 `LockAbortedError`
70
+ */
71
+ readonly destroy: () => void;
72
+ }
73
+ export type { LockDriver, LockDriverDeps };
File without changes
@@ -0,0 +1,123 @@
1
+ /**
2
+ * WebLocksDriver:基于 `navigator.locks` 的跨 Tab 互斥锁实现(首选 driver)
3
+ *
4
+ * 适用场景(由 pickDriver 决定):
5
+ * - `mode === 'auto'` 且运行时检测到 `navigator.locks`(现代浏览器 / Chromium 内核环境)
6
+ * - `mode === 'web-locks'` 强制指定
7
+ *
8
+ * 实现要点(对应 RFC.md「WebLocksDriver(首选)」):
9
+ * - 核心 API:`navigator.locks.request(name, { mode: 'exclusive', steal, signal }, callback)`
10
+ * - `callback` 持锁期间必须返回一个 Promise;锁会一直持有直到该 Promise settle
11
+ * → 这里构造一个外部可 resolve 的 `holdPromise`,`LockHandle.release` 就是 resolve 它
12
+ * - `force: true` → `steal: true`;原持有者的 callback 会以 `AbortError` reject,
13
+ * 捕获后把原 handle 的 `onRevokedByDriver('force')` 触发
14
+ * - `acquireTimeout` → `AbortController.abort()`;`navigator.locks.request` 会 reject
15
+ * `AbortError`(注意需要与 steal 场景区分:本 handle 还没拿到锁就 abort,而非被抢)
16
+ * - `signal.aborted`(外部 signal)同 `acquireTimeout` 通过合并 AbortController 统一处理
17
+ * - `destroy`:对所有仍持有锁的 handle 调用 release;等待中的 acquire 由各自的 signal
18
+ * 负责清理(destroy 会广播 abort 给内部 controller)
19
+ *
20
+ * 与其他 driver 的关键差异:
21
+ * - 互斥语义由浏览器保证(跨 Tab),无需自研排队协议
22
+ * - `release` 是**同步**触发(resolve holdPromise),但底层 navigator.locks 的清理
23
+ * 是微任务队列,所以上层看到的 release Promise 下一轮 tick 才 settle
24
+ */
25
+ import type { LockDriverContext } from '../types';
26
+ import type { LockDriver, LockDriverDeps } from './types';
27
+ /**
28
+ * Web Locks API 的最小化类型定义
29
+ *
30
+ * 为什么不直接用 `lib.dom` 内置 types:
31
+ * - TypeScript 4.x / 早期 5.x 的 lib.dom 对 `LockManager` 的定义不完整
32
+ * (缺 `steal` / `ifAvailable` 字段);本地定义保证 strict 下可编译
33
+ * - 仅声明 driver 内部实际用到的子集,不追求全面
34
+ */
35
+ interface WebLockRequestOptions {
36
+ mode?: 'exclusive' | 'shared';
37
+ ifAvailable?: boolean;
38
+ steal?: boolean;
39
+ signal?: AbortSignal;
40
+ }
41
+ interface WebLockManager {
42
+ readonly request: <T>(name: string, options: WebLockRequestOptions, callback: (lock: unknown) => Promise<T> | T) => Promise<T>;
43
+ }
44
+ /**
45
+ * 从运行时获取 `navigator.locks`;不存在则返回 null
46
+ *
47
+ * 正常路径下 pickDriver 会在选中本 driver 前做能力检测,本函数兜底覆盖两种边缘场景:
48
+ * 1. 上层绕过 pickDriver 直接实例化本 driver(如单元测试)
49
+ * 2. 运行时 `navigator` 对象缺失(非浏览器环境 + Node 22+ 的部分运行时)
50
+ * 返回 null 时由 `createWebLocksDriver` 构造期抛错,不会静默降级
51
+ */
52
+ declare function getWebLockManager(): WebLockManager | null;
53
+ /**
54
+ * 内部持有态:一次成功 acquire 产生一条
55
+ *
56
+ * - `holdPromise`:callback 期间返回给 navigator.locks 的 Promise;resolve 时锁释放
57
+ * - `resolveHold`:从外部 resolve holdPromise 的闭包引用,`release` 调用它
58
+ * - `revokeCallback`:用户(actions 层)通过 `onRevokedByDriver` 注册的回调
59
+ * - `released`:幂等开关
60
+ */
61
+ interface WebLockHolding {
62
+ readonly token: string;
63
+ readonly holdPromise: Promise<void>;
64
+ readonly resolveHold: () => void;
65
+ revokeCallback: ((reason: 'force' | 'timeout') => void) | null;
66
+ released: boolean;
67
+ }
68
+ /**
69
+ * 把 `ctx.signal` 与 `acquireTimeout` 合并为单一 AbortSignal 传给 navigator.locks
70
+ *
71
+ * 为什么需要合并:
72
+ * - navigator.locks.request 只接受单个 signal 作为"放弃抢锁"的开关
73
+ * - 需要同时响应"外部 signal abort"与"acquireTimeout 到期"两条路径
74
+ * - 用 `AbortController` 手动合并比 `AbortSignal.any` 兼容面更广
75
+ *
76
+ * 返回的 cleanup 必须在 `navigator.locks.request` settle 后调用,清理 timer + listener
77
+ */
78
+ declare function mergeSignalWithTimeout(externalSignal: AbortSignal, acquireTimeout: LockDriverContext['acquireTimeout'], token: string): {
79
+ signal: AbortSignal;
80
+ cleanup: () => void;
81
+ getTimeoutFired: () => boolean;
82
+ };
83
+ /** 判定错误是否属于 navigator.locks abort 类型(DOMException 'AbortError') */
84
+ declare function isAbortLikeError(error: unknown): boolean;
85
+ /** 顶层辅助函数共享的 driver 级依赖容器 */
86
+ interface DriverScope {
87
+ readonly holdings: Set<WebLockHolding>;
88
+ readonly logger: LockDriverDeps['logger'];
89
+ readonly driverName: string;
90
+ }
91
+ /**
92
+ * 已拿到锁后被 steal / force 驱逐的处理路径
93
+ *
94
+ * W3C 规范:原持有者的 `navigator.locks.request` 返回 Promise 以 AbortError reject;
95
+ * 必须显式 `resolveHold()` 避免 callback 里的 holdPromise 永远挂起(虽然 navigator.locks
96
+ * 此时已释放锁,但不 resolve 会造成本地 Promise 泄漏 + 后续 release 的幂等判定失效)
97
+ *
98
+ * 提至模块顶层,通过 `scope` 容器传入 driver 级依赖,降低 `createWebLocksDriver` 的
99
+ * linesPerFunction(biome noExcessiveLinesPerFunction = 100)
100
+ */
101
+ declare function handleStealRejection(seized: WebLockHolding, scope: DriverScope): void;
102
+ /**
103
+ * 处理 navigator.locks.request 的 settle —— 分三种情况:
104
+ * 1. resolve 路径(正常 release)→ 兜底清理 holding
105
+ * 2. reject + 已持有 → steal 路径,触发 onRevokedByDriver('force')
106
+ * 3. reject + 未持有 → 未拿到锁就被 abort / 非法参数等,把错误传给 acquire 入口
107
+ */
108
+ declare function wireRequestSettle(requestPromise: Promise<unknown>, getHolding: () => WebLockHolding | null, rejectGranted: (error: unknown) => void, scope: DriverScope): void;
109
+ declare function createWebLocksDriver(deps: LockDriverDeps): LockDriver;
110
+ /**
111
+ * destroy 时排空 holdings:把所有未 released 的 holding 同步释放
112
+ *
113
+ * 提至模块顶层的原因:
114
+ * - `holding.released === true` 的 false 分支在正常运行路径下不可达(release/handleStealRejection
115
+ * 都同步从 holdings 删除 + 置 released=true),属于防御性死代码
116
+ * - 抽出顶层后允许测试直接构造混合 holdings 集合(含已 released 的元素),命中防御分支
117
+ *
118
+ * 复制一份再遍历,避免 resolveHold 触发的副作用修改 `holdings`
119
+ * 用 Array.from + for 循环代替 forEach —— biome.useIterableCallbackReturn 禁止 forEach 回调有返回值
120
+ */
121
+ declare function drainHoldingsOnDestroy(holdings: Set<WebLockHolding>): void;
122
+ export type { DriverScope, WebLockHolding, WebLockManager };
123
+ export { createWebLocksDriver, drainHoldingsOnDestroy, getWebLockManager, handleStealRejection, isAbortLikeError, mergeSignalWithTimeout, wireRequestSettle, };
@@ -0,0 +1 @@
1
+ import{throwError as e}from"../../throw-error/index.js";import{isFunction as r,isNumber as t,isObject as o}from"../../utils/index.js";import{withResolvers as l}from"../../with-resolvers/index.js";import{ERROR_FN_NAME as n}from"../constants.js";import{LockAbortedError as i,LockTimeoutError as a}from"../errors/index.js";function s(){if("u"<typeof navigator)return null;let{locks:e}=navigator;return e||null}function d(e,r,o){let l=new AbortController,i=!1,s=null;if(e.aborted)return l.abort(e.reason),{signal:l.signal,cleanup:()=>void 0,getTimeoutFired:()=>i};function d(){l.abort(e.reason)}return e.addEventListener("abort",d,{once:!0}),t(r)&&r>0&&(s=setTimeout(()=>{i=!0,l.abort(new a(`[@cmtlyt/lingshu-toolkit#${n}]: acquire timed out after ${r}ms (token=${o})`))},r)),{signal:l.signal,cleanup:function(){null!==s&&(clearTimeout(s),s=null),e.removeEventListener("abort",d)},getTimeoutFired:()=>i}}function u(e){if(!o(e))return!1;let{name:r}=e;return"AbortError"===r}function c(e,t){if(e.released)return;let{holdings:o,logger:l,driverName:n}=t;if(e.released=!0,o.delete(e),e.resolveHold(),l.debug(`[${n}] web-locks driver: revoked by steal token=${e.token}`),r(e.revokeCallback))try{e.revokeCallback("force")}catch(e){l.error(`[${n}] web-locks driver: revoke callback threw`,e)}}function k(e,r,t,o){let{holdings:l}=o;e.then(()=>{let e=r();e&&!e.released&&(e.released=!0,l.delete(e))}).catch(e=>{let l=r();l?c(l,o):t(e)})}function v(r){let{name:t,logger:o}=r,c=s();c||e(n,"web-locks driver requires navigator.locks; use auto mode or fallback driver",TypeError);let v=new Set,m=!1;return{acquire:async function r(r){m&&e(n,"web-locks driver has been destroyed",i);let{signal:s,cleanup:b,getTimeoutFired:f}=d(r.signal,r.acquireTimeout,r.token),g=l(),$=null,w=l(),h=!0===r.force?{mode:"exclusive",steal:!0}:{mode:"exclusive",signal:s};k(c.request(t,h,()=>($={token:r.token,holdPromise:g.promise,resolveHold:g.resolve,revokeCallback:null,released:!1},v.add($),o.debug(`[${t}] web-locks driver: grant token=${r.token} steal=${!0===r.force}`),w.resolve($),g.promise)),()=>$,w.reject,{holdings:v,logger:o,driverName:t});try{var y;let l=await w.promise;return b(),m&&(l.released=!0,v.delete(l),l.resolveHold(),e(n,`web-locks driver destroyed during acquire (token=${r.token})`,i)),y=l,{release:()=>{y.released||(y.released=!0,v.delete(y),y.resolveHold(),o.debug(`[${t}] web-locks driver: release token=${y.token}`))},onRevokedByDriver:e=>{y.revokeCallback=e}}}catch(l){throw b(),f()&&e(n,`acquire timed out after ${String(r.acquireTimeout)}ms (token=${r.token})`,a,{cause:l}),(u(l)||r.signal.aborted)&&e(n,`acquire aborted (token=${r.token})`,i,{cause:l}),o.error(`[${t}] web-locks driver: request failed (token=${r.token})`,l),l}},destroy:function(){m||(m=!0,o.debug(`[${t}] web-locks driver: destroy (active holdings=${v.size})`),b(v))}}}function b(e){let r=Array.from(e);for(let t=0;t<r.length;t++){let o=r[t];o.released||(o.released=!0,e.delete(o),o.resolveHold())}}export{v as createWebLocksDriver,b as drainHoldingsOnDestroy,s as getWebLockManager,c as handleStealRejection,u as isAbortLikeError,d as mergeSignalWithTimeout,k as wireRequestSettle};
@@ -0,0 +1,12 @@
1
+ /**
2
+ * lock-data 错误类型的 barrel 导出
3
+ *
4
+ * 每个错误类拆分为独立文件,以遵循 biome 的 `noExcessiveClassesPerFile` 规则;
5
+ * 此文件仅做聚合导出,方便内部模块统一 import
6
+ */
7
+ export { InvalidOptionsError } from './invalid-options-error';
8
+ export { LockAbortedError } from './lock-aborted-error';
9
+ export { LockDisposedError } from './lock-disposed-error';
10
+ export { LockRevokedError } from './lock-revoked-error';
11
+ export { LockTimeoutError } from './lock-timeout-error';
12
+ export { ReadonlyMutationError } from './readonly-mutation-error';
@@ -0,0 +1 @@
1
+ export{InvalidOptionsError}from"./invalid-options-error.js";export{LockAbortedError}from"./lock-aborted-error.js";export{LockDisposedError}from"./lock-disposed-error.js";export{LockRevokedError}from"./lock-revoked-error.js";export{LockTimeoutError}from"./lock-timeout-error.js";export{ReadonlyMutationError}from"./readonly-mutation-error.js";
@@ -0,0 +1,11 @@
1
+ /**
2
+ * `options` 不合法时抛出(如 `timeout < 0`、未知的 `syncMode` 等)
3
+ *
4
+ * 归类为 TypeError 的原因:这是调用方传参问题,而非运行时故障,
5
+ * 与 JavaScript 原生对非法 API 用法抛 TypeError 的惯例对齐
6
+ * 对应 RFC.md「错误类型」章节
7
+ */
8
+ declare class InvalidOptionsError extends TypeError {
9
+ constructor(message?: string);
10
+ }
11
+ export { InvalidOptionsError };
@@ -0,0 +1 @@
1
+ class r extends TypeError{constructor(r){super(r),this.name="InvalidOptionsError"}}export{r as InvalidOptionsError};
@@ -0,0 +1,10 @@
1
+ /**
2
+ * `ActionCallOptions.signal` 在 acquiring / holding 阶段 abort 时抛出
3
+ *
4
+ * 与 `LockDisposedError` 区分:本错误仅影响当前调用,actions 实例仍可继续使用
5
+ * 对应 RFC.md「错误类型」章节
6
+ */
7
+ declare class LockAbortedError extends Error {
8
+ constructor(message?: string);
9
+ }
10
+ export { LockAbortedError };
@@ -0,0 +1 @@
1
+ class r extends Error{constructor(r){super(r),this.name="LockAbortedError"}}export{r as LockAbortedError};
@@ -0,0 +1,14 @@
1
+ /**
2
+ * 调用方在 actions 已 `dispose()` 后继续使用时抛出
3
+ *
4
+ * 同时覆盖:
5
+ * - `options.signal.aborted` 后任意调用
6
+ * - `options.getValue` Promise reject 后共享同一 Entry 的任意调用
7
+ * (此时错误 `cause` 字段携带原始 reject 原因)
8
+ *
9
+ * 对应 RFC.md「错误类型」章节
10
+ */
11
+ declare class LockDisposedError extends Error {
12
+ constructor(message?: string, options?: ErrorOptions);
13
+ }
14
+ export { LockDisposedError };
@@ -0,0 +1 @@
1
+ class r extends Error{constructor(r,o){super(r,o),this.name="LockDisposedError"}}export{r as LockDisposedError};
@@ -0,0 +1,10 @@
1
+ /**
2
+ * 持有锁期间被 `force` 抢占或 `holdTimeout` 触发时抛出
3
+ *
4
+ * 持有者后续对 draft 的写入也会继续抛出此错误,防止闭包泄露导致写入无效
5
+ * 对应 RFC.md「错误类型」章节
6
+ */
7
+ declare class LockRevokedError extends Error {
8
+ constructor(message?: string);
9
+ }
10
+ export { LockRevokedError };
@@ -0,0 +1 @@
1
+ class r extends Error{constructor(r){super(r),this.name="LockRevokedError"}}export{r as LockRevokedError};
@@ -0,0 +1,9 @@
1
+ /**
2
+ * 超过 `timeout` / `acquireTimeout` 仍未获得锁时抛出
3
+ *
4
+ * 对应 RFC.md「错误类型」章节
5
+ */
6
+ declare class LockTimeoutError extends Error {
7
+ constructor(message?: string);
8
+ }
9
+ export { LockTimeoutError };
@@ -0,0 +1 @@
1
+ class r extends Error{constructor(r){super(r),this.name="LockTimeoutError"}}export{r as LockTimeoutError};
@@ -0,0 +1,11 @@
1
+ /**
2
+ * 直接修改 `readonly` 视图时抛出
3
+ *
4
+ * 归类为 TypeError 的原因:与原生对 frozen 对象写入时的规范语义对齐,
5
+ * 便于业务代码用同一种 catch 分支处理"写入被阻止"类错误
6
+ * 对应 RFC.md「错误类型」章节
7
+ */
8
+ declare class ReadonlyMutationError extends TypeError {
9
+ constructor(message?: string);
10
+ }
11
+ export { ReadonlyMutationError };
@@ -0,0 +1 @@
1
+ class r extends TypeError{constructor(r){super(r),this.name="ReadonlyMutationError"}}export{r as ReadonlyMutationError};
@@ -0,0 +1,57 @@
1
+ import type { LockDataOptions, LockDataReturn, LockDataValueShape } from './types';
2
+ /**
3
+ * 创建一个带锁的数据容器(**单参数 API + getValue 必传 + 条件类型自动推断返回值**)
4
+ *
5
+ * 单签名 + 严格条件类型契约(对应 RFC.md「§3 核心语义 / §API 签名」+ 决策 #33 §A):
6
+ * - `getValue: () => T | Promise<T>` 必传 —— 数据来源唯一入口
7
+ * - `T` 必须满足 `LockDataValueShape<T> = T extends readonly unknown[] ? never : T`,
8
+ * 即 **类型层禁止顶层数组**(`lockData<string[]>` 在类型层即被排除为 `never`)
9
+ * - **返回值类型由 `LockDataReturn<T, O>` 条件类型从入参 `O` 直接推断**,无需调用方断言:
10
+ * 1. `O extends { syncMode: 'storage-authority' }` → 必须配 `id: string`,否则编译期 `never`(最严格)
11
+ * 2. 否则若 `ReturnType<O['getValue']> extends Promise<unknown>` → `Promise<LockDataTuple<T>>`
12
+ * 3. 否则 → `LockDataTuple<T>`
13
+ * - 同步抛错 → 同步抛 `LockDisposedError`(Entry 不构造)
14
+ * - 异步 reject → 返回的 Promise reject `LockDisposedError`,`cause` 字段携带原因
15
+ * - 运行时双重 fail-fast:`Array.isArray(awaited)` 拒绝顶层数组(抛 `InvalidOptionsError`),
16
+ * 非 JSON-safe 抛 `InvalidOptionsError`
17
+ *
18
+ * 返回元组:
19
+ * - 第一个元素:深只读视图(`ReadonlyView<T>`,wrapper Proxy 实现),业务只能读取不能直接写入
20
+ * - 第二个元素:actions(`update` / `replace` / `snapshot` / `dispose` 等),通过事务 API 修改数据
21
+ *
22
+ * ### 同步初始化(直接得元组,无需 await / 断言)
23
+ *
24
+ * ```ts
25
+ * const [view, actions] = lockData<{ count: number }>({
26
+ * getValue: () => ({ count: 0 }),
27
+ * });
28
+ * view.count; // 0
29
+ * await actions.update((draft) => { draft.count = 1; });
30
+ * ```
31
+ *
32
+ * ### 异步初始化(getValue 返回 Promise → 类型自动收窄为 Promise<LockDataTuple<T>>)
33
+ *
34
+ * ```ts
35
+ * const [view, actions] = await lockData<User>({
36
+ * getValue: () => fetch('/api/user').then((r) => r.json()),
37
+ * });
38
+ * ```
39
+ *
40
+ * ### 跨 Tab 同步(syncMode='storage-authority' 必须配 id,否则编译期报错)
41
+ *
42
+ * ```ts
43
+ * const [view, actions] = await lockData<{ count: number }>({
44
+ * id: 'shared-counter',
45
+ * syncMode: 'storage-authority',
46
+ * getValue: () => ({ count: 0 }),
47
+ * });
48
+ * ```
49
+ */
50
+ type LockDataInfer<O> = O extends {
51
+ getValue: () => infer R;
52
+ } ? Awaited<R> extends infer T extends object ? T : never : never;
53
+ type LockDataResolveReturn<O extends object> = LockDataValueShape<LockDataInfer<O>> extends infer T extends object ? LockDataReturn<T, O> : never;
54
+ declare function lockData<const O extends LockDataOptions<unknown>>(options: O): LockDataResolveReturn<O>;
55
+ export { NEVER_TIMEOUT } from './constants';
56
+ export { InvalidOptionsError, LockAbortedError, LockDisposedError, LockRevokedError, LockTimeoutError, ReadonlyMutationError, } from './errors';
57
+ export { lockData };
@@ -0,0 +1 @@
1
+ import{lockData as r}from"./core/entry.js";function o(o){let t=r(o);return t instanceof Promise,t}export{NEVER_TIMEOUT}from"./constants.js";export{InvalidOptionsError,LockAbortedError,LockDisposedError,LockRevokedError,LockTimeoutError,ReadonlyMutationError}from"./errors/index.js";export{o as lockData};