@abloatai/ablo 0.3.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 (278) hide show
  1. package/CHANGELOG.md +208 -0
  2. package/LICENSE +201 -0
  3. package/NOTICE +12 -0
  4. package/README.md +230 -0
  5. package/dist/BaseSyncedStore.d.ts +709 -0
  6. package/dist/BaseSyncedStore.js +1843 -0
  7. package/dist/Database.d.ts +344 -0
  8. package/dist/Database.js +1259 -0
  9. package/dist/LazyReferenceCollection.d.ts +181 -0
  10. package/dist/LazyReferenceCollection.js +460 -0
  11. package/dist/Model.d.ts +339 -0
  12. package/dist/Model.js +715 -0
  13. package/dist/ModelRegistry.d.ts +200 -0
  14. package/dist/ModelRegistry.js +535 -0
  15. package/dist/NetworkMonitor.d.ts +27 -0
  16. package/dist/NetworkMonitor.js +73 -0
  17. package/dist/ObjectPool.d.ts +202 -0
  18. package/dist/ObjectPool.js +1106 -0
  19. package/dist/SyncClient.d.ts +489 -0
  20. package/dist/SyncClient.js +1555 -0
  21. package/dist/SyncEngineContext.d.ts +46 -0
  22. package/dist/SyncEngineContext.js +74 -0
  23. package/dist/adapters/alwaysOnline.d.ts +16 -0
  24. package/dist/adapters/alwaysOnline.js +19 -0
  25. package/dist/adapters/inMemoryStorage.d.ts +30 -0
  26. package/dist/adapters/inMemoryStorage.js +94 -0
  27. package/dist/agent/Agent.d.ts +358 -0
  28. package/dist/agent/Agent.js +500 -0
  29. package/dist/agent/index.d.ts +115 -0
  30. package/dist/agent/index.js +128 -0
  31. package/dist/agent/session.d.ts +90 -0
  32. package/dist/agent/session.js +156 -0
  33. package/dist/agent/types.d.ts +73 -0
  34. package/dist/agent/types.js +10 -0
  35. package/dist/ai-sdk/coordination-context.d.ts +51 -0
  36. package/dist/ai-sdk/coordination-context.js +107 -0
  37. package/dist/ai-sdk/index.d.ts +68 -0
  38. package/dist/ai-sdk/index.js +68 -0
  39. package/dist/ai-sdk/intent-broadcast.d.ts +77 -0
  40. package/dist/ai-sdk/intent-broadcast.js +72 -0
  41. package/dist/ai-sdk/wrap.d.ts +67 -0
  42. package/dist/ai-sdk/wrap.js +45 -0
  43. package/dist/api/index.d.ts +10 -0
  44. package/dist/api/index.js +9 -0
  45. package/dist/auth/index.d.ts +137 -0
  46. package/dist/auth/index.js +246 -0
  47. package/dist/client/Ablo.d.ts +835 -0
  48. package/dist/client/Ablo.js +1440 -0
  49. package/dist/client/ApiClient.d.ts +200 -0
  50. package/dist/client/ApiClient.js +659 -0
  51. package/dist/client/auth.d.ts +79 -0
  52. package/dist/client/auth.js +81 -0
  53. package/dist/client/createInternalComponents.d.ts +44 -0
  54. package/dist/client/createInternalComponents.js +88 -0
  55. package/dist/client/createModelProxy.d.ts +152 -0
  56. package/dist/client/createModelProxy.js +199 -0
  57. package/dist/client/identity.d.ts +63 -0
  58. package/dist/client/identity.js +156 -0
  59. package/dist/client/index.d.ts +36 -0
  60. package/dist/client/index.js +33 -0
  61. package/dist/client/persistence.d.ts +7 -0
  62. package/dist/client/persistence.js +11 -0
  63. package/dist/client/validateAbloOptions.d.ts +42 -0
  64. package/dist/client/validateAbloOptions.js +43 -0
  65. package/dist/config/index.d.ts +10 -0
  66. package/dist/config/index.js +12 -0
  67. package/dist/context.d.ts +27 -0
  68. package/dist/context.js +58 -0
  69. package/dist/core/DatabaseManager.d.ts +108 -0
  70. package/dist/core/DatabaseManager.js +361 -0
  71. package/dist/core/QueryProcessor.d.ts +77 -0
  72. package/dist/core/QueryProcessor.js +262 -0
  73. package/dist/core/QueryView.d.ts +64 -0
  74. package/dist/core/QueryView.js +219 -0
  75. package/dist/core/StoreManager.d.ts +131 -0
  76. package/dist/core/StoreManager.js +334 -0
  77. package/dist/core/ViewRegistry.d.ts +20 -0
  78. package/dist/core/ViewRegistry.js +55 -0
  79. package/dist/core/index.d.ts +34 -0
  80. package/dist/core/index.js +59 -0
  81. package/dist/core/openIDBWithTimeout.d.ts +27 -0
  82. package/dist/core/openIDBWithTimeout.js +63 -0
  83. package/dist/core/query-utils.d.ts +37 -0
  84. package/dist/core/query-utils.js +60 -0
  85. package/dist/errors.d.ts +235 -0
  86. package/dist/errors.js +243 -0
  87. package/dist/index.d.ts +41 -0
  88. package/dist/index.js +82 -0
  89. package/dist/interfaces/headless.d.ts +95 -0
  90. package/dist/interfaces/headless.js +41 -0
  91. package/dist/interfaces/index.d.ts +321 -0
  92. package/dist/interfaces/index.js +8 -0
  93. package/dist/mutators/RecordingTransaction.d.ts +36 -0
  94. package/dist/mutators/RecordingTransaction.js +216 -0
  95. package/dist/mutators/Transaction.d.ts +48 -0
  96. package/dist/mutators/Transaction.js +64 -0
  97. package/dist/mutators/UndoManager.d.ts +114 -0
  98. package/dist/mutators/UndoManager.js +143 -0
  99. package/dist/mutators/defineMutators.d.ts +55 -0
  100. package/dist/mutators/defineMutators.js +28 -0
  101. package/dist/policy/index.d.ts +19 -0
  102. package/dist/policy/index.js +18 -0
  103. package/dist/policy/types.d.ts +74 -0
  104. package/dist/policy/types.js +17 -0
  105. package/dist/principal.d.ts +44 -0
  106. package/dist/principal.js +49 -0
  107. package/dist/query/client.d.ts +43 -0
  108. package/dist/query/client.js +84 -0
  109. package/dist/query/index.d.ts +6 -0
  110. package/dist/query/index.js +5 -0
  111. package/dist/query/types.d.ts +143 -0
  112. package/dist/query/types.js +36 -0
  113. package/dist/react/AbloProvider.d.ts +205 -0
  114. package/dist/react/AbloProvider.js +398 -0
  115. package/dist/react/ClientSideSuspense.d.ts +36 -0
  116. package/dist/react/ClientSideSuspense.js +17 -0
  117. package/dist/react/DefaultFallback.d.ts +24 -0
  118. package/dist/react/DefaultFallback.js +43 -0
  119. package/dist/react/SyncGroupProvider.d.ts +19 -0
  120. package/dist/react/SyncGroupProvider.js +44 -0
  121. package/dist/react/context.d.ts +161 -0
  122. package/dist/react/context.js +35 -0
  123. package/dist/react/index.d.ts +64 -0
  124. package/dist/react/index.js +73 -0
  125. package/dist/react/internalContext.d.ts +35 -0
  126. package/dist/react/internalContext.js +3 -0
  127. package/dist/react/useAblo.d.ts +72 -0
  128. package/dist/react/useAblo.js +63 -0
  129. package/dist/react/useCurrentUserId.d.ts +21 -0
  130. package/dist/react/useCurrentUserId.js +33 -0
  131. package/dist/react/useErrorListener.d.ts +20 -0
  132. package/dist/react/useErrorListener.js +39 -0
  133. package/dist/react/useIntent.d.ts +29 -0
  134. package/dist/react/useIntent.js +42 -0
  135. package/dist/react/useMutate.d.ts +83 -0
  136. package/dist/react/useMutate.js +122 -0
  137. package/dist/react/useMutationFailureListener.d.ts +26 -0
  138. package/dist/react/useMutationFailureListener.js +38 -0
  139. package/dist/react/useMutators.d.ts +56 -0
  140. package/dist/react/useMutators.js +66 -0
  141. package/dist/react/usePresence.d.ts +32 -0
  142. package/dist/react/usePresence.js +41 -0
  143. package/dist/react/useQuery.d.ts +123 -0
  144. package/dist/react/useQuery.js +145 -0
  145. package/dist/react/useReactive.d.ts +35 -0
  146. package/dist/react/useReactive.js +111 -0
  147. package/dist/react/useReader.d.ts +69 -0
  148. package/dist/react/useReader.js +73 -0
  149. package/dist/react/useSyncStatus.d.ts +61 -0
  150. package/dist/react/useSyncStatus.js +76 -0
  151. package/dist/react/useUndoScope.d.ts +36 -0
  152. package/dist/react/useUndoScope.js +73 -0
  153. package/dist/realtime/index.d.ts +10 -0
  154. package/dist/realtime/index.js +9 -0
  155. package/dist/schema/field.d.ts +134 -0
  156. package/dist/schema/field.js +264 -0
  157. package/dist/schema/index.d.ts +29 -0
  158. package/dist/schema/index.js +38 -0
  159. package/dist/schema/model.d.ts +326 -0
  160. package/dist/schema/model.js +89 -0
  161. package/dist/schema/queries.d.ts +203 -0
  162. package/dist/schema/queries.js +145 -0
  163. package/dist/schema/relation.d.ts +172 -0
  164. package/dist/schema/relation.js +104 -0
  165. package/dist/schema/schema.d.ts +259 -0
  166. package/dist/schema/schema.js +188 -0
  167. package/dist/schema/sugar.d.ts +129 -0
  168. package/dist/schema/sugar.js +94 -0
  169. package/dist/source/index.d.ts +423 -0
  170. package/dist/source/index.js +320 -0
  171. package/dist/source/pushQueue.d.ts +112 -0
  172. package/dist/source/pushQueue.js +249 -0
  173. package/dist/stores/ObjectStore.d.ts +103 -0
  174. package/dist/stores/ObjectStore.js +371 -0
  175. package/dist/stores/ObjectStoreContract.d.ts +39 -0
  176. package/dist/stores/ObjectStoreContract.js +1 -0
  177. package/dist/stores/SyncActionStore.d.ts +101 -0
  178. package/dist/stores/SyncActionStore.js +481 -0
  179. package/dist/sync/BootstrapHelper.d.ts +127 -0
  180. package/dist/sync/BootstrapHelper.js +434 -0
  181. package/dist/sync/ConnectionManager.d.ts +136 -0
  182. package/dist/sync/ConnectionManager.js +465 -0
  183. package/dist/sync/HydrationCoordinator.d.ts +137 -0
  184. package/dist/sync/HydrationCoordinator.js +468 -0
  185. package/dist/sync/NetworkProbe.d.ts +43 -0
  186. package/dist/sync/NetworkProbe.js +113 -0
  187. package/dist/sync/OfflineFlush.d.ts +9 -0
  188. package/dist/sync/OfflineFlush.js +22 -0
  189. package/dist/sync/OfflineTransactionStore.d.ts +37 -0
  190. package/dist/sync/OfflineTransactionStore.js +263 -0
  191. package/dist/sync/SyncWebSocket.d.ts +663 -0
  192. package/dist/sync/SyncWebSocket.js +1336 -0
  193. package/dist/sync/createIntentStream.d.ts +33 -0
  194. package/dist/sync/createIntentStream.js +243 -0
  195. package/dist/sync/createPresenceStream.d.ts +46 -0
  196. package/dist/sync/createPresenceStream.js +192 -0
  197. package/dist/sync/createSnapshot.d.ts +33 -0
  198. package/dist/sync/createSnapshot.js +124 -0
  199. package/dist/sync/participants.d.ts +114 -0
  200. package/dist/sync/participants.js +336 -0
  201. package/dist/sync/schemas.d.ts +79 -0
  202. package/dist/sync/schemas.js +78 -0
  203. package/dist/testing/fixtures/bootstrap.d.ts +45 -0
  204. package/dist/testing/fixtures/bootstrap.js +53 -0
  205. package/dist/testing/fixtures/deltas.d.ts +86 -0
  206. package/dist/testing/fixtures/deltas.js +139 -0
  207. package/dist/testing/fixtures/models.d.ts +82 -0
  208. package/dist/testing/fixtures/models.js +270 -0
  209. package/dist/testing/helpers/react-wrapper.d.ts +66 -0
  210. package/dist/testing/helpers/react-wrapper.js +64 -0
  211. package/dist/testing/helpers/sync-engine-harness.d.ts +55 -0
  212. package/dist/testing/helpers/sync-engine-harness.js +70 -0
  213. package/dist/testing/helpers/wait.d.ts +25 -0
  214. package/dist/testing/helpers/wait.js +44 -0
  215. package/dist/testing/index.d.ts +21 -0
  216. package/dist/testing/index.js +32 -0
  217. package/dist/testing/mocks/MockMutationExecutor.d.ts +65 -0
  218. package/dist/testing/mocks/MockMutationExecutor.js +139 -0
  219. package/dist/testing/mocks/MockNetworkMonitor.d.ts +20 -0
  220. package/dist/testing/mocks/MockNetworkMonitor.js +46 -0
  221. package/dist/testing/mocks/MockSyncContext.d.ts +64 -0
  222. package/dist/testing/mocks/MockSyncContext.js +100 -0
  223. package/dist/testing/mocks/MockSyncStore.d.ts +88 -0
  224. package/dist/testing/mocks/MockSyncStore.js +171 -0
  225. package/dist/testing/mocks/MockWebSocket.d.ts +66 -0
  226. package/dist/testing/mocks/MockWebSocket.js +117 -0
  227. package/dist/transactions/OptimisticEchoTracker.d.ts +82 -0
  228. package/dist/transactions/OptimisticEchoTracker.js +104 -0
  229. package/dist/transactions/TransactionQueue.d.ts +499 -0
  230. package/dist/transactions/TransactionQueue.js +1895 -0
  231. package/dist/transactions/index.d.ts +16 -0
  232. package/dist/transactions/index.js +7 -0
  233. package/dist/transactions/mutation-error-handler.d.ts +5 -0
  234. package/dist/transactions/mutation-error-handler.js +39 -0
  235. package/dist/types/global.d.ts +107 -0
  236. package/dist/types/global.js +38 -0
  237. package/dist/types/index.d.ts +241 -0
  238. package/dist/types/index.js +70 -0
  239. package/dist/types/streams.d.ts +495 -0
  240. package/dist/types/streams.js +11 -0
  241. package/dist/utils/asyncIterator.d.ts +41 -0
  242. package/dist/utils/asyncIterator.js +142 -0
  243. package/dist/utils/duration.d.ts +28 -0
  244. package/dist/utils/duration.js +47 -0
  245. package/dist/utils/mobx-setup.d.ts +42 -0
  246. package/dist/utils/mobx-setup.js +381 -0
  247. package/docs/api-keys.md +24 -0
  248. package/docs/api.md +230 -0
  249. package/docs/audit.md +81 -0
  250. package/docs/capabilities.md +163 -0
  251. package/docs/client-behavior.md +202 -0
  252. package/docs/data-sources.md +214 -0
  253. package/docs/examples/agent-human.md +84 -0
  254. package/docs/examples/ai-sdk-tool.md +92 -0
  255. package/docs/examples/existing-python-backend.md +249 -0
  256. package/docs/examples/nextjs.md +88 -0
  257. package/docs/examples/server-agent.md +86 -0
  258. package/docs/guarantees.md +148 -0
  259. package/docs/index.md +97 -0
  260. package/docs/integration-guide.md +493 -0
  261. package/docs/interaction-model.md +140 -0
  262. package/docs/mcp/claude-code.md +43 -0
  263. package/docs/mcp/cursor.md +53 -0
  264. package/docs/mcp/windsurf.md +46 -0
  265. package/docs/mcp.md +59 -0
  266. package/docs/quickstart.md +152 -0
  267. package/docs/react.md +115 -0
  268. package/docs/roadmap.md +45 -0
  269. package/examples/README.md +54 -0
  270. package/examples/data-source/README.md +102 -0
  271. package/examples/data-source/ablo-driver.ts +89 -0
  272. package/examples/data-source/customer-server.ts +208 -0
  273. package/examples/data-source/run.ts +101 -0
  274. package/examples/data-source/schema.ts +25 -0
  275. package/examples/quickstart.ts +54 -0
  276. package/examples/tsconfig.json +16 -0
  277. package/llms.txt +143 -0
  278. package/package.json +147 -0
@@ -0,0 +1,246 @@
1
+ /**
2
+ * Internal apiKey → capability exchange.
3
+ *
4
+ * Called by the `Ablo({...})` factory's `ready()` flow when the
5
+ * consumer passed `apiKey` without an explicit `capabilityToken` /
6
+ * `organizationId` / `user.id`. SDK calls `/auth/capability` once,
7
+ * server returns the scope + userMeta blobs (Phases 1A + 1B),
8
+ * SDK populates internal state from the response.
9
+ *
10
+ * Consumer never sees this happen. Same shape as Stripe / Anthropic
11
+ * SDKs hide their internal auth-handshake — the apiKey is the only
12
+ * credential the consumer touches.
13
+ */
14
+ import { AbloAuthenticationError } from '../errors.js';
15
+ export async function exchangeApiKey(options) {
16
+ if (!options.apiKey) {
17
+ throw new AbloAuthenticationError('apiKey is required for capability exchange', { code: 'apikey_missing' });
18
+ }
19
+ if (!options.baseUrl) {
20
+ throw new AbloAuthenticationError('baseUrl is required for capability exchange', { code: 'base_url_missing' });
21
+ }
22
+ const fetcher = options.fetch ?? fetch;
23
+ const url = `${options.baseUrl.replace(/\/+$/, '')}/auth/capability`;
24
+ const timeoutMs = options.timeoutMs ?? 10_000;
25
+ const controller = new AbortController();
26
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
27
+ let response;
28
+ try {
29
+ response = await fetcher(url, {
30
+ method: 'POST',
31
+ headers: {
32
+ 'Content-Type': 'application/json',
33
+ Authorization: `Bearer ${options.apiKey}`,
34
+ },
35
+ body: JSON.stringify({
36
+ participantKind: options.participantKind,
37
+ ...(options.participantId ? { participantId: options.participantId } : {}),
38
+ syncGroups: options.syncGroups,
39
+ operations: options.operations,
40
+ wideScope: options.wideScope,
41
+ ttlSeconds: options.ttlSeconds,
42
+ label: options.label,
43
+ userMeta: options.userMeta,
44
+ }),
45
+ signal: controller.signal,
46
+ });
47
+ }
48
+ catch (err) {
49
+ throw new AbloAuthenticationError(`apiKey exchange failed: ${err instanceof Error ? err.message : String(err)}`, { code: 'exchange_network_error', cause: err });
50
+ }
51
+ finally {
52
+ clearTimeout(timer);
53
+ }
54
+ if (!response.ok) {
55
+ let body = null;
56
+ try {
57
+ body = await response.json();
58
+ }
59
+ catch {
60
+ // ignore — server returned non-JSON error
61
+ }
62
+ const errBody = body;
63
+ throw new AbloAuthenticationError(`apiKey exchange rejected (${response.status}): ${errBody?.reason ?? response.statusText}`, {
64
+ code: errBody?.error ?? 'exchange_failed',
65
+ httpStatus: response.status,
66
+ });
67
+ }
68
+ const raw = (await response.json());
69
+ if (!isCapabilityExchangeResponse(raw)) {
70
+ throw new AbloAuthenticationError('apiKey exchange response was malformed — missing required fields', { code: 'exchange_malformed_response' });
71
+ }
72
+ return raw;
73
+ }
74
+ function isCapabilityExchangeResponse(raw) {
75
+ if (!raw || typeof raw !== 'object')
76
+ return false;
77
+ const o = raw;
78
+ if (typeof o.token !== 'string')
79
+ return false;
80
+ if (typeof o.expiresAt !== 'string')
81
+ return false;
82
+ if (typeof o.organizationId !== 'string')
83
+ return false;
84
+ if (typeof o.scope !== 'object' || o.scope === null)
85
+ return false;
86
+ if (typeof o.userMeta !== 'object' || o.userMeta === null)
87
+ return false;
88
+ return true;
89
+ }
90
+ /**
91
+ * Resolve the caller's Ablo identity from the authenticated request
92
+ * context. Used by browser/session/capability flows where the SDK should
93
+ * not require a public `userId` prop just to open local storage.
94
+ */
95
+ export async function resolveIdentity(options) {
96
+ if (!options.baseUrl) {
97
+ throw new AbloAuthenticationError('baseUrl is required for identity resolve', {
98
+ code: 'base_url_missing',
99
+ });
100
+ }
101
+ const fetcher = options.fetch ?? fetch;
102
+ const url = `${options.baseUrl.replace(/\/+$/, '')}/auth/identity`;
103
+ const timeoutMs = options.timeoutMs ?? 10_000;
104
+ const controller = new AbortController();
105
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
106
+ let response;
107
+ try {
108
+ const headers = { Accept: 'application/json' };
109
+ if (options.authToken) {
110
+ headers.Authorization = `Bearer ${options.authToken}`;
111
+ }
112
+ response = await fetcher(url, {
113
+ method: 'GET',
114
+ headers,
115
+ credentials: 'include',
116
+ signal: controller.signal,
117
+ });
118
+ }
119
+ catch (err) {
120
+ throw new AbloAuthenticationError(`identity resolve failed: ${err instanceof Error ? err.message : String(err)}`, { code: 'identity_network_error', cause: err });
121
+ }
122
+ finally {
123
+ clearTimeout(timer);
124
+ }
125
+ if (!response.ok) {
126
+ let body = null;
127
+ try {
128
+ body = await response.json();
129
+ }
130
+ catch {
131
+ // ignore non-JSON auth errors
132
+ }
133
+ const errBody = body;
134
+ throw new AbloAuthenticationError(`identity resolve rejected (${response.status}): ${errBody?.reason ?? response.statusText}`, {
135
+ code: errBody?.error ?? 'identity_resolve_failed',
136
+ httpStatus: response.status,
137
+ });
138
+ }
139
+ return (await response.json());
140
+ }
141
+ const DEFAULT_BUFFER_FLOOR_MS = 60_000;
142
+ const DEFAULT_BUFFER_RATIO = 0.1;
143
+ export function createRefreshScheduler(options) {
144
+ const now = options.now ?? Date.now;
145
+ const setTimer = options.setTimer ?? setTimeout;
146
+ const clearTimer = options.clearTimer ?? clearTimeout;
147
+ let expiresAtMs = options.initialExpiresAtMs;
148
+ let timer = null;
149
+ let inFlight = null;
150
+ let disposed = false;
151
+ // Default visibility attach: only when running in a browser-like env.
152
+ // The Node-side agent worker never has `document`, so the default
153
+ // does the right thing without explicit opt-out.
154
+ const wantsVisibility = options.attachVisibilityListener ?? true;
155
+ const hasDocument = typeof document !== 'undefined';
156
+ const visibilityActive = wantsVisibility && hasDocument;
157
+ function bufferFor(currentExpiresAtMs) {
158
+ if (typeof options.bufferMs === 'number')
159
+ return options.bufferMs;
160
+ const ttl = currentExpiresAtMs - now();
161
+ return Math.max(DEFAULT_BUFFER_FLOOR_MS, Math.floor(ttl * DEFAULT_BUFFER_RATIO));
162
+ }
163
+ function clearTimerIfAny() {
164
+ if (timer !== null) {
165
+ clearTimer(timer);
166
+ timer = null;
167
+ }
168
+ }
169
+ function schedule() {
170
+ if (disposed)
171
+ return;
172
+ clearTimerIfAny();
173
+ const buffer = bufferFor(expiresAtMs);
174
+ const delay = Math.max(0, expiresAtMs - buffer - now());
175
+ timer = setTimer(() => {
176
+ void refreshNow().catch(() => {
177
+ // onError already fired inside refreshNow; swallow here so
178
+ // the timer callback doesn't surface as an unhandled rejection.
179
+ });
180
+ }, delay);
181
+ }
182
+ function refreshNow() {
183
+ if (disposed) {
184
+ return Promise.reject(new Error('refreshScheduler: disposed'));
185
+ }
186
+ if (inFlight)
187
+ return inFlight;
188
+ inFlight = (async () => {
189
+ try {
190
+ const result = await options.refresh();
191
+ if (disposed)
192
+ return result;
193
+ expiresAtMs = result.expiresAtMs;
194
+ options.onRefreshed?.({ expiresAtMs });
195
+ schedule();
196
+ return result;
197
+ }
198
+ catch (err) {
199
+ const error = err instanceof Error ? err : new Error(String(err));
200
+ options.onError?.(error);
201
+ // Reschedule even on failure so the next window still triggers.
202
+ // The user's apiKey may have been temporarily unreachable.
203
+ if (!disposed)
204
+ schedule();
205
+ throw error;
206
+ }
207
+ finally {
208
+ inFlight = null;
209
+ }
210
+ })();
211
+ return inFlight;
212
+ }
213
+ function onVisibilityChange() {
214
+ if (disposed)
215
+ return;
216
+ if (typeof document === 'undefined')
217
+ return;
218
+ if (document.visibilityState !== 'visible')
219
+ return;
220
+ const buffer = bufferFor(expiresAtMs);
221
+ if (expiresAtMs - now() <= buffer) {
222
+ void refreshNow().catch(() => {
223
+ // already routed through onError
224
+ });
225
+ }
226
+ }
227
+ if (visibilityActive) {
228
+ document.addEventListener('visibilitychange', onVisibilityChange);
229
+ }
230
+ schedule();
231
+ return {
232
+ refreshNow,
233
+ dispose() {
234
+ if (disposed)
235
+ return;
236
+ disposed = true;
237
+ clearTimerIfAny();
238
+ if (visibilityActive) {
239
+ document.removeEventListener('visibilitychange', onVisibilityChange);
240
+ }
241
+ },
242
+ get expiresAtMs() {
243
+ return expiresAtMs;
244
+ },
245
+ };
246
+ }